-
2
module Origen
-
2
class Application
-
2
class Configuration
-
2
require 'pathname'
-
-
# Returns the configuration's application instance
-
2
attr_reader :app
-
-
2
attr_accessor :name, :initials, :instructions,
-
:history_file, :release_directory, :release_email_subject,
-
:production_targets,
-
:vault, :output_directory, :reference_directory,
-
:semantically_version, :log_directory, :pattern_name_translator,
-
:pattern_directory, :pattern_output_directory, :pattern_prefix, :pattern_postfix,
-
:pattern_header, :default_lsf_action, :release_instructions, :proceed_with_pattern,
-
:test_program_output_directory, :erb_trim_mode, :test_program_source_directory,
-
:test_program_template_directory, :referenced_pattern_list, :program_prefix,
-
:copy_command, :diff_command, :compile_only_dot_erb_files, :web_directory,
-
:web_domain,
-
:strict_errors, :unmanaged_dirs, :unmanaged_files, :remotes,
-
:external_app_dirs, :lint_test, :shared, :yammer_group, :rc_url, :rc_workflow,
-
:user_aliases, :release_externally, :gem_name, :disqus_shortname,
-
:default_plugin, :rc_tag_prepend_v
-
-
# Mark any attributes that are likely to depend on properties of the target here,
-
# this will raise an error if they are ever accessed before the target has been
-
# instantiated (a concern for Origen core developers only).
-
#
-
# These attributes will also receive an enhanced accessor that accepts a block, see
-
# below for more details on this.
-
2
ATTRS_THAT_DEPEND_ON_TARGET = [
-
:output_directory, :reference_directory, :pattern_postfix, :pattern_prefix,
-
:pattern_header, :current_plugin_pattern_header, :application_pattern_header, :shared_pattern_header,
-
:release_directory, :pattern_name_translator, :pattern_directory, :pattern_output_directory,
-
:proceed_with_pattern, :test_program_output_directory, :test_program_source_directory,
-
:test_program_template_directory, :referenced_pattern_list, :program_prefix, :web_directory,
-
:web_domain
-
]
-
-
# Any attributes that want to accept a block, but not necessarily require the target
-
# can be added here
-
2
ATTRS_THAT_DONT_DEPEND_ON_TARGET = [
-
:release_instructions, :history_file, :log_directory, :copy_command,
-
:diff_command, :remotes,
-
:external_app_dirs
-
]
-
-
# If a current plugin is present then its value for these attributes will be
-
# used instead of that from the current application
-
2
ATTRS_THAT_CURRENT_PLUGIN_CAN_OVERRIDE = [
-
:pattern_prefix, :pattern_postfix, :program_prefix, :pattern_header, :pattern_output_directory,
-
:output_directory, :reference_directory, :test_program_output_directory,
-
:test_program_template_directory, :referenced_pattern_list
-
]
-
-
2
ATTRS_THAT_ARE_SET_TO_A_BLOCK = [
-
:current_plugin_pattern_header, :application_pattern_header, :shared_pattern_header, #:pattern_footer
-
]
-
-
2
def log_deprecations
-
# unless imports.empty?
-
# Origen.deprecate "App #{app.name} uses config.imports this will be removed in Origen V3 and a Gemfile/.gemspec should be used instead"
-
# end
-
end
-
-
2
def initialize(app)
-
16
@app = app
-
16
@name = 'Unknown'
-
16
@initials = 'NA'
-
16
@semantically_version = false
-
16
@compile_only_dot_erb_files = true
-
# Functions used here since Origen.root is not available when this is first instantiated
-
27
@output_directory = -> { "#{Origen.root}/output" }
-
16
@reference_directory = lambda do
-
if Origen.config.output_directory.to_s =~ /(\\|\/)output(\\|\/)/
-
Origen.config.output_directory.to_s.sub(/(\\|\/)output(\\|\/)/, '\1.ref\2')
-
else
-
"#{Origen.root}/.ref"
-
end
-
end
-
16
@release_directory = -> { Origen.root }
-
16
@release_email_subject = false
-
56
@log_directory = -> { "#{Origen.root}/log" }
-
16
@pattern_name_translator = ->(name) { name }
-
41
@pattern_directory = -> { "#{Origen.root}/pattern" }
-
46
@pattern_output_directory = -> { Origen.app.config.output_directory }
-
16
@history_file = -> { "#{Origen.root}/doc/history" }
-
16
@default_lsf_action = :clear
-
69
@proceed_with_pattern = ->(_name) { true }
-
16
@erb_trim_mode = '%'
-
18
@referenced_pattern_list = -> { "#{Origen.root}/list/referenced.list" }
-
22
@copy_command = -> { Origen.running_on_windows? ? 'copy' : 'cp' }
-
16
@diff_command = -> { Origen.running_on_windows? ? 'start winmerge' : 'tkdiff' }
-
16
@imports = []
-
16
@imports_dev = []
-
16
@external_app_dirs = []
-
16
@unmanaged_dirs = []
-
16
@unmanaged_files = []
-
16
@remotes = []
-
16
@lint_test = {}
-
16
@user_aliases = {}
-
16
@rc_tag_prepend_v = true
-
end
-
-
# This defines an enhanced accessor for these attributes that allows them to be assigned
-
# to an anonymous function to calculate the value based on some property of the target
-
# objects.
-
#
-
# Without this the objects from the target could not be referenced in config/application.rb
-
# because they don't exist yet, for example this will not work because $dut has not yet
-
# been instantiated:
-
# # config/application.rb
-
#
-
# config.output_directory = "#{Origen.root}/output/#{$dut.class}"
-
#
-
# However this accessor provides a way to do that via the following syntax:
-
# # config/application.rb
-
#
-
# config.output_directory do
-
# "#{Origen.root}/output/#{$dut.class}"
-
# end
-
#
-
# Or on one line:
-
# # config/application.rb
-
#
-
# config.output_directory { "#{Origen.root}/output/#{$dut.class}" }
-
#
-
# Or if you prefer the more explicit:
-
# # config/application.rb
-
#
-
# config.output_directory = ->{ "#{Origen.root}/output/#{$dut.class}" }
-
2
def self.add_attribute(name, options = {})
-
options = {
-
54
depend_on_target: true
-
}.merge(options)
-
54
attr_writer name
-
54
define_method name do |override = true, &block|
-
4241
if block # _given?
-
10
instance_variable_set("@#{name}".to_sym, block)
-
else
-
4231
if override && ATTRS_THAT_CURRENT_PLUGIN_CAN_OVERRIDE.include?(name) &&
-
app.current? && Origen.app.plugins.current
-
1053
var = Origen.app.plugins.current.config.send(name, override: false)
-
end
-
4231
var ||= instance_variable_get("@#{name}".to_sym) || options[:default]
-
4231
if var.respond_to?('call')
-
723
if options[:depend_on_target]
-
# If an attempt has been made to access this attribute before the target has
-
# been instantiated raise an error
-
# Note Origen.app here instead of just app to ensure we are talking to the top level application,
-
# that is the only one that has a target
-
677
unless Origen.app.target_instantiated?
-
fail "You have attempted to access Origen.config.#{name} before instantiating the target"
-
end
-
end
-
-
# Some config variables should be left as a block/proc object. If this is one of those, just return the var.
-
723
ATTRS_THAT_ARE_SET_TO_A_BLOCK.include?(name) ? var : var.call
-
else
-
3508
var
-
end
-
end
-
end
-
end
-
-
42
ATTRS_THAT_DEPEND_ON_TARGET.each { |n| add_attribute(n) }
-
-
16
ATTRS_THAT_DONT_DEPEND_ON_TARGET.each { |n| add_attribute(n, depend_on_target: false) }
-
-
2
(ATTRS_THAT_CURRENT_PLUGIN_CAN_OVERRIDE - ATTRS_THAT_DEPEND_ON_TARGET - ATTRS_THAT_DONT_DEPEND_ON_TARGET).each do |name|
-
if override && ATTRS_THAT_CURRENT_PLUGIN_CAN_OVERRIDE.include?(name) &&
-
app.current? && Origen.app.plugins.current
-
var = Origen.app.plugins.current.config.send(name, override: false)
-
end
-
var || instance_variable_get("@#{name}".to_sym)
-
end
-
-
2
def pattern_name_translator(name = nil, &block)
-
5
if block
-
4
@pattern_name_translator = block
-
else
-
1
@pattern_name_translator.call(name)
-
end
-
end
-
-
2
def proceed_with_pattern(name = nil, &block)
-
53
if block
-
@proceed_with_pattern = block
-
else
-
53
@proceed_with_pattern.call(name)
-
end
-
end
-
-
# Add a new pattern iterator
-
2
def pattern_iterator
-
6
yield Origen.generator.create_iterator
-
end
-
-
2
def lsf
-
3
app.lsf.configuration
-
end
-
-
# Prevent a new attribute from a future version of Origen from dying before the
-
# user can be prompted to upgrade
-
2
def method_missing(method, *_args, &_block)
-
method = method.to_s.sub('=', '')
-
Origen.log.warning "WARNING - unknown configuration attribute in #{app.name}: #{method}"
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
class Application
-
# This class manages deploying an application's website.
-
#
-
# The web pages are compiled in the local application workspace and
-
# deploy consists of copying them to the remote location.
-
#
-
# Two directories are maintained in the remote location, one containing the live
-
# website and another where the new site is copied to during a deploy.
-
# A symlink is used to indicate which one of the two directories is currently being
-
# served.
-
#
-
# Upon a successful copy the symlink is switched over, thereby providing zero-downtime
-
# deploys and guaranteeing that the old site will stay up if an error is encountered
-
# during a deploy.
-
#
-
# An alternative method of deploying is also supported by pushing to a Git repository.
-
1
class Deployer
-
1
require 'fileutils'
-
-
1
attr_writer :directory, :test
-
-
# Prepare for deploying, this will raise an error if the current user is found to
-
# have insufficient permissions to deploy to the target directory
-
1
def prepare!(options = {})
-
if deploy_to_git?
-
require 'highline/import'
-
@commit_message = options[:message] || options[:comment] || ask('Enter a deployment commit message: ') do |q|
-
q.validate = /\w/
-
q.responses[:not_valid] = "Can't be blank"
-
end
-
Origen.log.info "Fetching the website's Git respository..."
-
git_repo
-
begin
-
fail unless git_repo.can_checkin?
-
rescue
-
puts "Sorry, but you don't have permission to write to #{Origen.config.web_directory}!"
-
exit 1
-
end
-
else
-
begin
-
require_remote_directories
-
test_file = "#{Origen.config.web_directory}/_test_file.txt"
-
FileUtils.rm_f(test_file) if File.exist?(test_file)
-
FileUtils.touch(test_file)
-
FileUtils.rm_f(test_file)
-
rescue
-
puts "Sorry, but you don't have permission to write to #{Origen.config.web_directory}!"
-
exit 1
-
end
-
end
-
end
-
-
1
def git_sub_dir
-
17
if Origen.config.web_directory =~ /\.git\/(.*)$/
-
17
Regexp.last_match(1)
-
end
-
end
-
-
# Returns a RevisionControl::Git object that points to a local copy of the website repo
-
# which is will build and checkout as required
-
1
def git_repo
-
@git_repo ||= begin
-
local_path = "#{Origen.config.web_directory.gsub('/', '-').symbolize}"
-
local_path.gsub!(':', '-') if Origen.os.windows?
-
local = Pathname.new("#{Origen.app.workspace_manager.imports_directory}/git/#{local_path}")
-
if git_sub_dir
-
remote = Origen.config.web_directory.sub("\/#{git_sub_dir}", '')
-
else
-
remote = Origen.config.web_directory
-
end
-
git = RevisionControl::Git.new(local: local, remote: remote)
-
if git.initialized?
-
git.checkout(force: true)
-
else
-
git.build(force: true)
-
end
-
git
-
end
-
end
-
-
1
def test_run?
-
@test
-
end
-
-
1
def deploy_to_git?
-
17
!!(Origen.config.web_directory =~ /\.git\/?#{git_sub_dir}$/)
-
end
-
-
1
def require_remote_directories
-
%w(remote1 remote2).each do |dir|
-
dir = "#{Origen.config.web_directory}/#{dir}"
-
unless File.exist?(dir)
-
FileUtils.mkdir_p dir
-
end
-
end
-
end
-
-
1
def latest_symlink
-
"#{Origen.config.web_directory}/latest"
-
end
-
-
1
def offline_remote_directory
-
if File.exist?(latest_symlink)
-
if File.readlink(latest_symlink) =~ /remote1/
-
"#{Origen.config.web_directory}/remote2"
-
else
-
"#{Origen.config.web_directory}/remote1"
-
end
-
else
-
"#{Origen.config.web_directory}/remote1"
-
end
-
end
-
-
1
def live_remote_directory
-
if File.exist?(latest_symlink)
-
if File.readlink(latest_symlink) =~ /remote1/
-
"#{Origen.config.web_directory}/remote1"
-
elsif File.readlink(latest_symlink) =~ /remote2/
-
"#{Origen.config.web_directory}/remote2"
-
end
-
end
-
end
-
-
# Deploy a whole web site.
-
#
-
# This copies the entire contents of web/output in the application
-
# directory to the remote server.
-
1
def deploy_site
-
Origen.app.listeners_for(:before_deploy_site).each(&:before_deploy_site)
-
if deploy_to_git?
-
dir = git_repo.local.to_s
-
dir += "/#{git_sub_dir}" if git_sub_dir
-
# Delete everything so that we don't preserve old files
-
git_repo.delete_all(git_sub_dir)
-
FileUtils.mkdir_p(dir) unless File.exist?(dir)
-
`chmod a+w -R #{Origen.root}/web/output` # Ensure world writable, required?
-
FileUtils.cp_r "#{Origen.root}/web/output/.", dir
-
git_repo.checkin git_sub_dir, unmanaged: true, comment: @commit_message
-
else
-
# Empty the contents of the remote dir
-
if File.exist?(offline_remote_directory)
-
FileUtils.remove_dir(offline_remote_directory, true)
-
require_remote_directories
-
end
-
# Copy the new contents across
-
`chmod g+w -R #{Origen.root}/web/output` # Ensure group writable
-
FileUtils.cp_r "#{Origen.root}/web/output/.", offline_remote_directory
-
`chmod g+w -R #{offline_remote_directory}` # Double ensure group writable
-
# Make live
-
create_symlink offline_remote_directory, latest_symlink
-
index = "#{Origen.config.web_directory}/index.html"
-
# This symlink allows the site homepage to be accessed from the web root
-
# directory rather than root directory/latest
-
unless File.exist?(index)
-
create_symlink "#{latest_symlink}/index.html", index
-
end
-
end
-
end
-
-
1
def deploy_file(file)
-
remote_dir = deploy_to_git? ? "#{git_repo.local}/#{git_sub_dir}" : live_remote_directory
-
if remote_dir
-
file = Origen.file_handler.clean_path_to(file)
-
sub_dir = Origen.file_handler.sub_dir_of(file, "#{Origen.root}/templates/web") .to_s
-
page = file.basename.to_s.sub(/\..*/, '')
-
# Special case for the main index page
-
if page == 'index' && sub_dir == '.'
-
FileUtils.cp "#{Origen.root}/web/output/index.html", remote_dir
-
file = "#{remote_dir}/index.html"
-
else
-
FileUtils.mkdir_p("#{remote_dir}/#{sub_dir}/#{page}")
-
file = "#{remote_dir}/#{sub_dir}/#{page}/index.html"
-
FileUtils.cp "#{Origen.root}/web/output/#{sub_dir}/#{page}/index.html", file
-
end
-
if deploy_to_git?
-
git_repo.checkin file, unmanaged: true, comment: @commit_message
-
end
-
end
-
end
-
-
1
def generate_api
-
dir = "#{Origen.root}/web/output/api"
-
FileUtils.rm_rf(dir) if File.exist?(dir)
-
# system("cd #{Origen.root} && rdoc --op api --tab-width 4 --main api_doc/README.txt --title 'Origen (#{Origen.version})' api_doc lib/origen")
-
if Origen.root == Origen.top
-
title = "#{Origen.config.name} #{Origen.version}"
-
else
-
title = "#{Origen.config.name} #{Origen.app.version}"
-
end
-
system("yard doc --output-dir #{Origen.root}/web/output/api --title '#{title}'")
-
# Yard doesn't have an option to ignore github-style READMEs, so force it here to
-
# always present the API index on the API homepage for consistency
-
index = "#{Origen.root}/web/output/api/index.html"
-
_index = "#{Origen.root}/web/output/api/_index.html"
-
FileUtils.rm_f(index) if File.exist?(index)
-
# This removes a prominent link that we are left with to a README file that doesn't work
-
require 'nokogiri'
-
doc = Nokogiri::HTML(File.read(_index))
-
doc.xpath('//h2[contains(text(), "File Listing")]').remove
-
doc.css('#files').remove
-
File.open(_index, 'w') { |f| f.write(doc.to_html) }
-
FileUtils.cp(_index, index)
-
end
-
-
1
def deploy_archive(id)
-
dir = live_remote_directory
-
if dir
-
id.gsub!('.', '_')
-
archive_dir = "#{Origen.config.web_directory}/#{id}"
-
FileUtils.rm_rf(archive_dir) if File.exist?(archive_dir)
-
FileUtils.mkdir_p archive_dir
-
FileUtils.cp_r "#{Origen.root}/web/output/.", archive_dir
-
end
-
end
-
-
1
def create_symlink(from, to)
-
`rm -f #{to}` if File.exist?(to)
-
`ln -s #{from} #{to}` if File.exist?(from)
-
end
-
-
1
def web_server_dir
-
"#{Origen.root}/web"
-
end
-
-
1
def create_web_server_dir
-
templates_web_dir = 'app/templates/web'
-
templates_web_dir = 'templates/web' unless File.exist?("#{Origen.root}/#{templates_web_dir}")
-
if File.exist?("#{Origen.root}/#{templates_web_dir}")
-
dir = web_server_dir
-
FileUtils.rm_rf dir if File.exist?(dir)
-
FileUtils.mkdir_p dir
-
# Copy the web infrastructure
-
FileUtils.cp_r Dir.glob("#{Origen.top}/templates/nanoc/*").sort, dir
-
# Compile the dynamic stuff
-
Origen.app.runner.launch action: :compile,
-
files: "#{Origen.top}/templates/nanoc_dynamic",
-
output: dir
-
unless Origen.root == Origen.top
-
# Copy any application overrides if they exist
-
if File.exist?("#{Origen.root}/templates/nanoc")
-
FileUtils.cp_r Dir.glob("#{Origen.root}/templates/nanoc/*").sort, dir, remove_destination: true
-
end
-
end
-
@nanoc_dir = dir
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
class Application
-
# Class to control the environment.
-
#
-
# The environment is a Ruby file that is loaded prior to generating every piece of output.
-
# It is optional, and is loaded before the target, thereby allowing targets to override
-
# environment settings.
-
#
-
# A typical use case for the environment is to setup the test platform, or to set Origen
-
# to run in debug or simulation mode. It can generally be thought of as a global target.
-
#
-
# All environment definition files must live in Origen.root/environment.
-
#
-
# An instance of this class is automatically
-
# instantiated and available globally as Origen.environment.
-
2
class Environment
-
2
DIR = "#{Origen.root}/environment" # :nodoc:
-
2
SAVE_FILE = "#{DIR}/.default" # :nodoc:
-
2
DEFAULT_FILE = "#{DIR}/default.rb" # :nodoc:
-
-
# Returns the name (the filename) of the current environment
-
2
def name
-
38
file.basename('.rb').to_s if file
-
end
-
-
# Returns Array of all environments available
-
2
def all_environments
-
3
envs = []
-
3
find('').sort.each do |file|
-
6
envs << File.basename(file)
-
end
-
3
envs
-
end
-
-
# Returns true if the environment exists, this can be used to test for the presence
-
# of an environment before calling one of the other methods to actually apply it.
-
#
-
# It will return true if one or more environments are found matching the given name,
-
# use the unique? method to test if the given name uniquely identifies a valid
-
# environment.
-
2
def exists?(name)
-
3
envs = find(name)
-
3
envs.size > 0
-
end
-
2
alias_method :exist?, :exists?
-
-
# Similar to the exists? method, this will return true only if the given name
-
# resolves to a single valid environment.
-
2
def unique?(name)
-
3
envs = find(name)
-
3
envs.size == 1
-
end
-
-
# Switch to the supplied environment, name can be a fragment as long as it allows
-
# a unique environment to be identified.
-
#
-
# Calling this method does not affect the default environment setting in the workspace.
-
2
def temporary=(name)
-
3
envs = find(name)
-
3
if envs.size == 0
-
puts "Sorry no environments were found matching '#{name}'!"
-
puts 'Here are the available options:'
-
find('').sort.each do |file|
-
puts File.basename(file)
-
end
-
exit 1
-
3
elsif envs.size > 1
-
puts 'Please try again with one of the following environments:'
-
envs.sort.each do |file|
-
puts File.basename(file)
-
end
-
exit 1
-
else
-
3
self.file = envs[0]
-
end
-
end
-
2
alias_method :switch, :temporary=
-
2
alias_method :switch_to, :temporary=
-
-
# As #temporary= except that the given environment will be set to the workspace default
-
2
def default=(name)
-
2
if name
-
self.temporary = name
-
else
-
2
@file = nil
-
end
-
2
save
-
end
-
-
# Prints out the current environment details to the command line
-
2
def describe
-
f = self.file!
-
puts "Current environment: #{f.basename}"
-
puts '*' * 70
-
File.open(f).each do |line|
-
puts " #{line}"
-
end
-
puts '*' * 70
-
end
-
-
# Returns an array of matching environment file paths
-
2
def find(name)
-
12
if name
-
12
name = name.gsub('*', '')
-
12
if File.exist?(name)
-
[name]
-
12
elsif File.exist?("#{Origen.root}/environment/#{name}") && name != ''
-
["#{Origen.root}/environment/#{name}"]
-
else
-
# The below weirdness is to make it recurse into symlinked directories
-
12
Dir.glob("#{DIR}/**{,/*/**}/*").sort.uniq.select do |file|
-
25
File.basename(file) =~ /#{name}/ && file !~ /.*\.rb.+$/
-
end
-
end
-
else
-
[nil]
-
end
-
end
-
-
# Saves the current environment as the workspace default, i.e. the current environment
-
# will be used by Origen the next time if no other environment is specified
-
2
def save # :nodoc:
-
2
if @file
-
File.open(SAVE_FILE, 'w') do |f|
-
Marshal.dump(file, f)
-
end
-
else
-
2
forget
-
end
-
end
-
-
# Load the default file from the workspace default if it exists and return it,
-
# otherwise returns nil
-
2
def default_file
-
521
return @default_file if @default_file
-
521
if File.exist?(SAVE_FILE)
-
File.open(SAVE_FILE) do |f|
-
@default_file = Marshal.load(f)
-
end
-
521
elsif File.exist?(DEFAULT_FILE)
-
@default_file = Pathname.new(DEFAULT_FILE)
-
end
-
521
@default_file
-
end
-
-
# Returns the environment file (a Pathname object) if it has been defined, otherwise nil
-
2
def file # :nodoc:
-
525
return @file if @file
-
521
if default_file && File.exist?(default_file)
-
@file = default_file
-
end
-
end
-
-
# As file except will raise an exception if it hasn't been defined yet
-
2
def file! # :nodoc:
-
unless file
-
puts 'No environment has been specified!'
-
puts 'To specify an environment use the -e switch.'
-
puts 'Look in the environment directory for a list of available environment names.'
-
exit 1
-
end
-
file
-
end
-
-
2
def file=(path) # :nodoc:
-
3
if path
-
3
@file = Pathname.new(path)
-
else
-
@file = nil
-
end
-
end
-
-
# Remove the workspace default environment
-
2
def forget
-
2
File.delete(SAVE_FILE) if File.exist?(SAVE_FILE)
-
2
@default_file = nil
-
end
-
-
# Returns true if running with a temporary environment, i.e. if the current
-
# environment is not the same as the default environment
-
2
def temporary?
-
@file == @default_file
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
class Application
-
# Responsible for handling all submissions to the LSF
-
1
class LSF
-
# The LSF command configuration that will be used for all submissions to
-
# the LSF. An instance of this class is returned via the configuration
-
# method and which can be used to modify the LSF behavior on a per-setup
-
# basis.
-
1
class Configuration
-
# The group parameter, default: nil
-
1
attr_accessor :group
-
# The project parameter, default: 'msg.te'
-
1
attr_accessor :project
-
# The resource parameter, default: 'linux'
-
1
attr_accessor :resource
-
# The queue parameter, default: 'short'
-
1
attr_accessor :queue
-
# When set to true no submissions will be made to LSF and instead the
-
# command that would have been submitted is printed to the terminal instead
-
1
attr_accessor :debug
-
# Specify the number of cores to use while submitting the job to LSF
-
# There is a restriction on the number of cores available per queue name
-
# Below is a table:
-
# Queue name equivalent Purpose
-
# interq gui Interactive jobs, like Virtuoso. Max 15 jobs/user
-
# batchq normal CPU intensive batch jobs, 1 .. 3 threads. Specify # of threads with bsub -n option. Slots/user: ~10% of total batch capacity.
-
# batchq_mt normal CPU intensive batch jobs, >= 4 threads. Specify # of threads with bsub -n option. Slots: shared with batchq.
-
# shortq short CPU intensive batch jobs, 1 thread (= 1 core), guaranteed run time 15 minutes. Slots/user: approximately 3x limit in batchq.
-
# offloadq - Used for offloading cpu intensive batch jobs to cloud, see CloudPortal.
-
# Do not submit directly into this queue. No real slot limit. Focused on CPU intensive jobs, not using much memory/data.
-
# distributed normal Run jobs than span multiple hosts.
-
# - prio High prio queue with low slot count, useful if you don't have slots available in normal queue. See PrioritizingMyJobs.
-
# - ondemand On-Demand Servers to satisfy urgent and short-term (2 weeks or less) customer compute requirements.
-
# - wam WAM cron processing
-
# - grid Low-priority batch jobs (random sim, regressions, etc). Access to all spare CPU cycles.
-
1
attr_accessor :cores
-
-
1
def initialize
-
1
@group = Origen.site_config.lsf_group
-
1
@project = Origen.site_config.lsf_project
-
1
@resource = Origen.site_config.lsf_resource
-
1
@queue = Origen.site_config.lsf_queue
-
1
@debug = Origen.site_config.lsf_debug
-
1
@cores = Origen.site_config.lsf_cores
-
end
-
end
-
-
# Accessor for the global LSF configuration, use this to modify the default
-
# LSF configuration for a given setup. Typically an alternate configuration would
-
# be added to the SoC class or the target file, but it can be set from anywhere.
-
# This method returns an instance of Origen::Application::LSF::Configuration and can
-
# be used as shown in the example.
-
#
-
# Example
-
# # soc/nevis.rb
-
#
-
# Origen::Runner::LSF.configuration do |config|
-
# # Use "msg.nevis" for the project string when running in Noida
-
# if %x["domainname"] =~ /nidc/
-
# config.lsf.project = "msg.nevis"
-
# end
-
# end
-
#
-
# # Change the default group
-
# Origen.config.lsf.group = "lam"
-
1
def self.configuration
-
3
@config ||= Configuration.new
-
3
yield @config if block_given?
-
3
@config
-
end
-
-
# Returns the configuration for a given LSF instance, which always maps to the
-
# global configuration instance.
-
1
def configuration
-
3
self.class.configuration
-
end
-
1
alias_method :config, :configuration
-
-
# Submits the given command to the LSF, returns the LSF job ID
-
1
def submit(command, options = {})
-
options = {
-
dependents: [],
-
rerunnable: true, # Will rerun automatically if the execution host fails
-
}.merge(options)
-
limit_job_submissions do
-
group = options[:group] || config.group
-
group = group ? "-G #{group}" : ''
-
project = options[:project] || config.project
-
project = project ? "-P #{project}" : ''
-
resource = options[:resource] || config.resource
-
resource = resource ? "-R '#{resource}'" : ''
-
queue = options[:queue] || config.queue
-
queue = queue ? "-q #{queue}" : ''
-
cores = options[:cores] || config.cores
-
cores = cores ? "-n #{cores}" : ''
-
rerunnable = options[:rerunnable] ? '-r' : ''
-
if options[:dependents].empty?
-
dependents = ''
-
else
-
dependents = options[:dependents].map { |id| "ended(#{id})" }.join(' && ')
-
dependents = "-w '#{dependents}'"
-
end
-
cmd = "bsub -oo /dev/null #{dependents} #{rerunnable} #{group} #{project} #{resource} #{queue} #{cores} '#{command}'"
-
if config.debug
-
puts cmd
-
'496212' # Return a dummy ID to keep the caller happy
-
else
-
output = `#{cmd}`
-
Origen.log.info output.strip
-
if output.split("\n").last =~ /Job <(\d+)> is submitted/
-
Regexp.last_match[1]
-
else
-
:error
-
end
-
end
-
end
-
end
-
-
1
def queuing_job_ids
-
ids = []
-
`bjobs 2>&1`.split("\n").each do |line|
-
if line =~ /^(\d+).*PEND/
-
ids << Regexp.last_match[1]
-
end
-
end
-
ids
-
end
-
-
1
def running_job_ids
-
ids = []
-
`bjobs 2>&1`.split("\n").each do |line|
-
if line =~ /^(\d+).*RUN/
-
ids << Regexp.last_match[1]
-
end
-
end
-
ids
-
end
-
-
1
def remote_jobs_count
-
i = 0
-
`bjobs 2>&1`.split("\n").each do |line|
-
if line =~ /^(\d+).*(RUN|PEND)/
-
i += 1
-
end
-
end
-
i
-
end
-
-
# Limits the number of jobs submitted to the LSF at one time, IT will start
-
# to warn if a single users current job count gets above 500.
-
# This method prevents that stage from being reached.
-
1
def limit_job_submissions
-
@local_job_count ||= 0
-
if @local_job_count == 100
-
while remote_jobs_count > 400
-
puts 'Waiting for submitted jobs count to fall below limit...'
-
sleep 5
-
end
-
@local_job_count = 0
-
yield
-
else
-
@local_job_count += 1
-
yield
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
class Application
-
# This class is responsible for co-ordinating and monitoring all submissions
-
# to the LSF. This is in contrast to Origen::Application::LSF which is an API for
-
# talking to the LSF.
-
2
class LSFManager
-
2
include Callbacks
-
-
# This will be set by the command dispatcher to reflect the currently executing
-
# command. If LSF jobs are spawned with the same command then any options passed
-
# to the parent command will automatically be forwarded to the children.
-
2
attr_accessor :current_command
-
-
2
def initialize
-
2
unless File.exist?(log_file_directory)
-
FileUtils.mkdir_p(log_file_directory)
-
end
-
end
-
-
# Picks and returns either the application's LSF instance or the global LSF instance
-
2
def lsf
-
if Origen.running_globally?
-
Origen.lsf!
-
else
-
Origen.app.lsf
-
end
-
end
-
-
2
def remote_jobs_file
-
"#{Origen.root}/.lsf/remote_jobs"
-
end
-
-
# Waits for all jobs to complete, will retry lost jobs (optionally
-
# failed jobs).
-
#
-
# Alternatively supply an :id or an array of :ids to wait only
-
# for specific job(s) to complete.
-
2
def wait_for_completion(options = {})
-
options = {
-
max_lost_retries: 10,
-
max_fail_retries: 0,
-
poll_duration_in_seconds: 10,
-
timeout_in_seconds: 3600
-
}.merge(options)
-
options[:start_time] ||= Time.now
-
if Time.now - options[:start_time] < options[:timeout_in_seconds]
-
# When waiting for ids we will hold by monitoring for the result
-
# files directly, rather than using the generatic classify routine.
-
# This is because the most common use case for this is when jobs
-
# are idling remotely on the LSF and don't want to run into contention
-
# issues when multiple processes try to classify/save the status.
-
if options[:id] || options[:ids]
-
ids = extract_ids([options[:id], options[:ids]].flatten.compact)
-
if ids.any? { |id| job_running?(id) }
-
sleep options[:poll_duration_in_seconds]
-
wait_for_completion(options)
-
end
-
-
else
-
classify_jobs
-
print_status(print_insructions: false)
-
sleep options[:poll_duration_in_seconds]
-
classify_jobs
-
resumitted = false
-
lost_jobs.each do |job|
-
if job[:submissions] < options[:max_lost_retries] + 1
-
resubmit_job(job)
-
resumitted = true
-
end
-
end
-
failed_jobs.each do |job|
-
if job[:submissions] < options[:max_fail_retries] + 1
-
resubmit_job(job)
-
resumitted = true
-
end
-
end
-
classify_jobs
-
if outstanding_jobs? || resumitted
-
wait_for_completion(options)
-
else
-
print_status
-
end
-
end
-
end
-
end
-
-
2
def print_status(options = {})
-
options = {
-
print_insructions: true
-
}.merge(options)
-
if options[:verbose]
-
print_details(options)
-
end
-
Origen.log.info ''
-
Origen.log.info 'LSF Status'
-
Origen.log.info '----------'
-
Origen.log.info "Queuing: #{queuing_jobs.size}"
-
Origen.log.info "Running: #{running_jobs.size}"
-
Origen.log.info "Lost: #{lost_jobs.size}"
-
Origen.log.info ''
-
Origen.log.info "Passed: #{passed_jobs.size}"
-
Origen.log.info "Failed: #{failed_jobs.size}"
-
Origen.log.info ''
-
if options[:print_insructions]
-
Origen.log.info 'Common tasks'
-
Origen.log.info '------------'
-
if queuing_jobs.size > 0
-
Origen.log.info 'Queuing'
-
Origen.log.info ' Show details: origen l -v -t queuing'
-
Origen.log.info ' Re-submit: origen l -r -t queuing'
-
end
-
if running_jobs.size > 0
-
Origen.log.info 'Running'
-
Origen.log.info ' Show details: origen l -v -t running'
-
Origen.log.info ' Re-submit: origen l -r -t running'
-
end
-
if lost_jobs.size > 0
-
Origen.log.info 'Lost'
-
Origen.log.info ' Show details: origen l -v -t lost'
-
Origen.log.info ' Re-submit: origen l -r -t lost'
-
end
-
if passed_jobs.size > 0
-
Origen.log.info 'Passed'
-
Origen.log.info ' Build log: origen l -l'
-
end
-
if failed_jobs.size > 0
-
Origen.log.info 'Failed'
-
Origen.log.info ' Show details: origen l -v -t failed'
-
Origen.log.info ' Re-submit: origen l -r -t failed'
-
end
-
Origen.log.info ''
-
Origen.log.info 'Reset the LSF manager (clear all jobs): origen lsf -c -t all'
-
Origen.log.info ''
-
end
-
end
-
-
2
def print_details(options = {})
-
if options[:id]
-
Origen.log.info "Job: #{options[:id]}"
-
Origen.log.info '----' + '-' * options[:id].length
-
print_details_of(remote_jobs[options[:id]])
-
else
-
options[:type] ||= :all
-
if options[:type] == :all || options[:type] == :queuing
-
Origen.log.info ''
-
Origen.log.info 'Queuing'
-
Origen.log.info '-------'
-
queuing_jobs.each { |j| print_details_of(j) }
-
end
-
if options[:type] == :all || options[:type] == :running
-
Origen.log.info ''
-
Origen.log.info 'Running'
-
Origen.log.info '-------'
-
running_jobs.each { |j| print_details_of(j) }
-
end
-
if options[:type] == :all || options[:type] == :lost
-
Origen.log.info ''
-
Origen.log.info 'Lost'
-
Origen.log.info '----'
-
lost_jobs.each { |j| print_details_of(j) }
-
end
-
if options[:type] == :all || options[:type] == :passed
-
Origen.log.info ''
-
Origen.log.info 'Passed'
-
Origen.log.info '------'
-
passed_jobs.each { |j| print_details_of(j) }
-
end
-
if options[:type] == :all || options[:type] == :failed
-
Origen.log.info ''
-
Origen.log.info 'Failed'
-
Origen.log.info '------'
-
failed_jobs.each { |j| print_details_of(j) }
-
end
-
end
-
end
-
-
2
def print_details_of(job)
-
Origen.log.info "#{job[:command]} #{job[:switches]}".gsub(' --exec_remote', '')
-
Origen.log.info "ID: #{job[:id]}"
-
Origen.log.info "Submitted: #{time_ago(job[:submitted_at])}"
-
Origen.log.info ''
-
end
-
-
2
def time_ago(time)
-
seconds = (Time.now - time).to_i
-
if seconds < 60
-
unit = 'second'
-
number = seconds
-
elsif seconds < 3600
-
unit = 'minute'
-
number = seconds / 60
-
elsif seconds < 86_400
-
unit = 'hour'
-
number = seconds / 3600
-
else
-
unit = 'day'
-
number = seconds / 86_400
-
end
-
"#{number} #{unit}#{number > 1 ? 's' : ''} ago"
-
end
-
-
2
def outstanding_jobs?
-
(running_jobs + queuing_jobs).size > 0
-
end
-
-
# Clear jobs from memory
-
2
def clear(options)
-
if options[:type]
-
if options[:type] == :all
-
File.delete(remote_jobs_file) if File.exist?(remote_jobs_file)
-
@remote_jobs = {}
-
return
-
else
-
send("#{options[:type]}_jobs").each do |job|
-
remote_jobs.delete(job[:id])
-
end
-
end
-
else
-
remote_jobs.delete(options[:id])
-
end
-
end
-
-
2
def clear_all
-
File.delete(remote_jobs_file) if File.exist?(remote_jobs_file)
-
if File.exist?(log_file_directory)
-
FileUtils.rm_rf(log_file_directory)
-
end
-
FileUtils.mkdir_p(log_file_directory)
-
@remote_jobs = {}
-
clear_caches
-
end
-
-
# Resubmit jobs
-
2
def resubmit(options)
-
if options[:type]
-
if options[:type] == :all
-
remote_jobs.each do |_id, job|
-
resubmit_job(job)
-
end
-
else
-
send("#{options[:type]}_jobs").each do |job|
-
resubmit_job(job)
-
end
-
end
-
else
-
resubmit_job(remote_jobs[options[:id]])
-
end
-
end
-
-
2
def stats
-
Origen.app.stats
-
end
-
-
# Build the log file from the completed jobs
-
2
def build_log(_options = {})
-
Origen.log.info '*' * 70
-
completed_jobs.each do |job|
-
File.open(log_file(job[:id])) do |f|
-
last_line_blank = false
-
f.readlines.each do |line|
-
# Capture and combine the per job stats that look like this:
-
# Total patterns: 1 1347 0.003674
-
# New patterns: 0
-
# Changed patterns: 1
-
# FAILED patterns: 1
-
# Total files: 1
-
# New files: 0
-
# Changed files: 0
-
# FAILED files: 1
-
begin
-
line.gsub!(/\e\[\d+m/, '') # Remove any coloring
-
if line =~ /Total patterns:\s+(\d+)/
-
stats.completed_patterns += Regexp.last_match[1].to_i
-
elsif line =~ /Total vectors:\s+(\d+)/
-
stats.total_vectors += Regexp.last_match[1].to_i
-
elsif line =~ /Total duration:\s+(\d+\.\d+)/
-
stats.total_duration += Regexp.last_match[1].to_f
-
elsif line =~ /Total files:\s+(\d+)/
-
stats.completed_files += Regexp.last_match[1].to_i
-
elsif line =~ /Changed patterns:\s+(\d+)/
-
stats.changed_patterns += Regexp.last_match[1].to_i
-
elsif line =~ /Changed files:\s+(\d+)/
-
stats.changed_files += Regexp.last_match[1].to_i
-
elsif line =~ /New patterns:\s+(\d+)/
-
stats.new_patterns += Regexp.last_match[1].to_i
-
elsif line =~ /New files:\s+(\d+)/
-
stats.new_files += Regexp.last_match[1].to_i
-
elsif line =~ /FAILED patterns:\s+(\d+)/
-
stats.failed_patterns += Regexp.last_match[1].to_i
-
elsif line =~ /FAILED files:\s+(\d+)/
-
stats.failed_files += Regexp.last_match[1].to_i
-
elsif line =~ /ERROR!/
-
stats.errors += 1
-
Origen.log.send :relog, line
-
else
-
# Compress multiple blank lines
-
if line =~ /^\s*$/ || line =~ /.*\|\|\s*$/
-
unless last_line_blank
-
Origen.log.info
-
last_line_blank = true
-
end
-
else
-
# Screen std origen output
-
unless line =~ / origen save/ ||
-
line =~ /Insecure world writable dir/ ||
-
line =~ /To save all of/
-
line.strip!
-
# line.sub!(/.*\|\| /, '')
-
Origen.log.send :relog, line
-
last_line_blank = false
-
end
-
end
-
end
-
rescue
-
# Sometimes illegal UTF-8 characters can get into crash dumps, if this
-
# happens just print the line out rather than die
-
Origen.log.error line
-
end
-
end
-
end
-
end
-
Origen.log.info '*' * 70
-
stats.print_summary
-
end
-
-
# Returns the logfile that should be used by a given process on the LSF, this
-
# should be be guaranteed to be unique
-
2
def log_file(id)
-
"#{log_file_directory}/#{log_file_name(id)}"
-
end
-
-
2
def passed_file(id)
-
"#{log_file_directory}/#{log_file_name(id)}.passed"
-
end
-
-
2
def started_file(id)
-
"#{log_file_directory}/#{log_file_name(id)}.started"
-
end
-
-
2
def failed_file(id)
-
"#{log_file_directory}/#{log_file_name(id)}.failed"
-
end
-
-
2
def log_file_name(id)
-
# host = `hostname`.strip
-
"#{id}.txt"
-
end
-
-
2
def log_file_directory
-
2
"#{Origen.root}/.lsf/remote_logs"
-
end
-
-
# Register that the given job ID has completed successfully on the LSF
-
2
def job_passed(id)
-
`touch #{passed_file(id)}`
-
end
-
-
# Register that the given job ID has failed on the LSF
-
2
def job_failed(id)
-
`touch #{failed_file(id)}`
-
end
-
-
2
def job_started(id)
-
`touch #{started_file(id)}`
-
end
-
-
2
def resubmit_job(job)
-
[log_file(job[:id]), passed_file(job[:id]), failed_file(job[:id]), started_file(job[:id])].each do |file|
-
FileUtils.rm_f(file) if File.exist?(file)
-
end
-
job[:lsf_id] = lsf.submit(command_prefix(job[:id], job[:dependents_ids]) + job[:command] + job[:switches], dependents: job[:dependents_lsf_ids])
-
job[:status] = nil
-
job[:completed_at] = nil
-
job[:submitted_at] = Time.now
-
job[:submissions] += 1
-
end
-
-
2
def submit_job(command, options = {})
-
options = {
-
lsf_option_string: ''
-
}.merge(options)
-
switches = [' ', options[:lsf_option_string], command_options(command)].flatten.compact.join(' ')
-
id = generate_job_id
-
dependents_ids = extract_ids([options[:depend], options[:depends], options[:dependent], options[:dependents]].flatten.compact)
-
dependents_lsf_ids = dependents_ids.map { |dep_id| remote_jobs[dep_id][:lsf_id] }
-
lsf_id = lsf.submit(command_prefix(id, dependents_ids) + command + switches, dependents: dependents_lsf_ids)
-
job_attrs = {
-
id: id,
-
lsf_id: lsf_id,
-
command: command,
-
submitted_at: Time.now,
-
submissions: 1,
-
switches: switches,
-
dependents_ids: dependents_ids,
-
dependents_lsf_ids: dependents_lsf_ids
-
}
-
remote_jobs[id] = job_attrs
-
end
-
-
2
def extract_ids(jobs_or_ids)
-
jobs_or_ids.map { |j| j.is_a?(Hash) ? j[:id] : j }
-
end
-
-
2
def submit_origen_job(cmd, options = {})
-
if options[:action]
-
action = options[:action] == :pattern ? ' generate' : " #{options[:action]}"
-
else
-
action = ''
-
end
-
-
str = "#{action} #{cmd}".strip
-
str.sub('origen ', '') if str =~ /^origen /
-
-
# Append the --exec_remote switch to all Origen commands, this allows command
-
# processing to be altered based on whether it is running locally or
-
# remotely by testing Origen.running_remotely?
-
str += ' --exec_remote'
-
-
submit_job("origen #{str}", options)
-
end
-
-
2
def command_prefix(id, dependents)
-
# define prefix as a blank string if Origen.site_config.lsf_command_prefix is not defined
-
if Origen.site_config.lsf_command_prefix
-
prefix = Origen.site_config.lsf_command_prefix
-
else
-
prefix = ''
-
end
-
prefix += "cd #{Origen.root}; origen l --execute --id #{id} "
-
unless dependents.empty?
-
prefix += "--dependents #{dependents.join(',')} "
-
end
-
prefix
-
end
-
-
2
def command_options(command_str)
-
command_str.sub(/origen\s*/, '') =~ /(\w+)/
-
command = Regexp.last_match[1]
-
command = ORIGEN_COMMAND_ALIASES[command] || command
-
if command == current_command
-
@command_options
-
else
-
''
-
end
-
end
-
-
# This will be called by the command dispatcher to record any options that were passed
-
# in when launching the current command.
-
# These will be automatically appended if the current command spawns any LSF jobs that
-
# will invoke the same command.
-
2
def command_options=(opts)
-
# Ensure these options are removed, these are either incompatible with the LSF,
-
# or will already have been added elsewhere
-
{
-
18
['-h', '--help'] => false,
-
['-w', '--wait'] => false,
-
['-d', '--debug'] => false,
-
['-c', '--continue'] => false,
-
'--exec_remote' => false,
-
['-t', '--target'] => '*',
-
['-e', '--environment'] => '*',
-
'--id' => '*',
-
['-l', '--lsf'] => %w(add clear)
-
}.each do |names, values|
-
162
[names].flatten.each do |name|
-
288
ix = opts.index(name)
-
288
if ix
-
15
opts.delete_at(ix)
-
15
[values].flatten.each do |value|
-
15
if value && (value == '*' || opts[ix] == value)
-
15
opts.delete_at(ix)
-
end
-
end
-
end
-
end
-
end
-
18
@command_options ||= []
-
18
@command_options += opts
-
end
-
-
2
def add_command_option(*opts)
-
@command_options ||= []
-
@command_options += opts
-
end
-
-
2
def remote_jobs
-
@remote_jobs ||= restore_remote_jobs || {}
-
end
-
-
2
def classify_jobs
-
clear_caches
-
queuing_job_ids = lsf.queuing_job_ids
-
running_job_ids = lsf.running_job_ids
-
remote_jobs.each do |_id, job|
-
# If the status has already been determined send it straight to the bucket
-
if job[:status]
-
send("#{job[:status]}_jobs") << job
-
else
-
if job[:lsf_id] == :error
-
job[:status] = :lost
-
lost_jobs << job
-
elsif job_completed?(job[:id])
-
if job_passed?(job[:id])
-
job[:status] = :passed
-
passed_jobs << job
-
elsif job_failed?(job[:id])
-
job[:status] = :failed
-
failed_jobs << job
-
end
-
else
-
if running_job_ids.include?(job[:lsf_id])
-
running_jobs << job
-
# Once we have assigned a job as running make sure the job is marked as started
-
# It can flicker back to queued if the started file takes a long time to arrive
-
# from the remote host
-
job_started(job[:lsf_id])
-
elsif queuing_job_ids.include?(job[:lsf_id])
-
queuing_jobs << job
-
elsif job_started?(job[:id])
-
# There can be considerable latency between the job writing the passed/failed
-
# file remotely and it showing up on the local machine.
-
# Give some buffer to that before declaring the file lost.
-
if job[:completed_at]
-
if (Time.now - job[:completed_at]) < 60
-
running_jobs << job
-
else
-
lost_jobs << job
-
end
-
else
-
job[:completed_at] = Time.now
-
running_jobs << job
-
end
-
# Give jobs submitted less than a minute ago the benefit of the
-
# doubt, they may not have shown up in bjobs yet
-
elsif (Time.now - job[:submitted_at]) < 60
-
queuing_jobs << job
-
else
-
lost_jobs << job
-
end
-
end
-
end
-
end
-
end
-
-
2
def clear_caches
-
@running_jobs = nil
-
@queuing_jobs = nil
-
@passed_jobs = nil
-
@failed_jobs = nil
-
@lost_jobs = nil
-
end
-
-
2
def running_jobs
-
@running_jobs ||= []
-
end
-
-
2
def queuing_jobs
-
@queuing_jobs ||= []
-
end
-
-
2
def completed_jobs
-
passed_jobs + failed_jobs
-
end
-
-
2
def passed_jobs
-
@passed_jobs ||= []
-
end
-
-
# Failed jobs are those that started to produce a log file but did not complete
-
2
def failed_jobs
-
@failed_jobs ||= []
-
end
-
-
# Lost jobs are ones that for whatever reason did not start, or at least get far
-
# enough to log that they started
-
2
def lost_jobs
-
@lost_jobs ||= []
-
end
-
-
# Returns trus if the given job ID generated a complete file when run on the LSF.
-
# The complete file is created at the end of a job run and its presence indicates
-
# that the job ran and got past the generation/compile stage without crashing.
-
2
def job_completed?(id)
-
job_started?(id) &&
-
(job_passed?(id) || job_failed?(id))
-
end
-
-
2
def job_running?(id)
-
!job_completed?(id)
-
end
-
-
2
def job_started?(id)
-
File.exist?(started_file(id))
-
end
-
-
2
def job_passed?(id)
-
File.exist?(passed_file(id))
-
end
-
-
2
def job_failed?(id)
-
File.exist?(failed_file(id))
-
end
-
-
2
def generate_job_id
-
"#{Time.now.to_f}".gsub('.', '')
-
end
-
-
2
def restore_remote_jobs
-
if File.exist?(remote_jobs_file)
-
File.open(remote_jobs_file) do |f|
-
begin
-
Marshal.load(f)
-
rescue
-
nil
-
end
-
end
-
end
-
end
-
-
2
def on_origen_shutdown(_options = {})
-
1
save_remote_jobs if @remote_jobs
-
end
-
-
2
def save_remote_jobs
-
File.open(remote_jobs_file, 'w') do |f|
-
Marshal.dump(@remote_jobs, f)
-
end
-
end
-
-
2
def execute_remotely(options = {})
-
job_started(options[:id])
-
begin
-
if options[:dependents]
-
wait_for_completion(ids: options[:dependents],
-
poll_duration_in_seconds: 1,
-
# Don't wait long by the time this runs the LSF
-
# should have guaranteed the job has run
-
timeout_in_seconds: 120
-
)
-
unless options[:dependents].all? { |id| job_passed?(id) }
-
File.open(log_file(options[:id]), 'w') do |f|
-
f.puts "*** ERROR! *** #{options[:cmd].join(' ')} ***"
-
f.puts 'Dependents failed!'
-
end
-
fail 'Dependents failed!'
-
end
-
end
-
if options[:cmd].is_a?(Array)
-
cmd = options[:cmd].join(' ')
-
else
-
cmd = options[:cmd]
-
end
-
output = `#{cmd} 2>&1`
-
File.open(log_file(options[:id]), 'w') do |f|
-
f.write output
-
end
-
if $CHILD_STATUS.success?
-
job_passed(options[:id])
-
else
-
job_failed(options[:id])
-
end
-
rescue
-
job_failed(options[:id])
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
class Application
-
# Provides an API for working with the application's plugins
-
#
-
# An instance of this class is instantiated as Origen.app.plugins
-
2
class Plugins < ::Array
-
2
def initialize
-
6
top = Origen.app
-
6
Origen._applications_lookup[:name].each do |_name, app|
-
48
self << app unless app == top
-
end
-
end
-
-
# Will raise an error if any plugins are currently imported from a path reference
-
# in the Gemfile
-
2
def validate_production_status(force = false)
-
392
if Origen.mode.production? || force
-
1
if File.exist?("#{Origen.root}/Gemfile")
-
1
File.readlines("#{Origen.root}/Gemfile").each do |line|
-
# https://rubular.com/r/yNGDGB6M2r
-
29
if line =~ /^\s*gem\s+(("|')\w+("|')),.*(:path\s*=>|path:)/
-
fail "The following gem is defined as a path in your Gemfile, but that is not allowed in production: #{Regexp.last_match[1]}"
-
end
-
29
if line =~ /ORIGEN PLUGIN AUTO-GENERATED/
-
fail 'Fetched gems are currently being used in your Gemfile, but that is not allowed in production!'
-
end
-
end
-
end
-
end
-
end
-
-
# Returns an array of symbols that represent the names of all plugins
-
2
def names
-
24
map(&:name)
-
end
-
-
# Returns the current plugin's application instance
-
2
def current
-
6083
return nil if @temporary == :none
-
5059
return nil if @disabled
-
5057
name = @temporary || @current ||= begin
-
1373
if Origen.app.session.origen_core[:default_plugin]
-
1
Origen.app.session.origen_core[:default_plugin]
-
1372
elsif Origen.app.config.default_plugin && !Origen.app.session.origen_core[:default_plugin_cleared_by_user]
-
1
Origen.app.config.default_plugin
-
end
-
end
-
10180
find { |p| p.name.to_sym == name } if name
-
end
-
-
2
def current=(name)
-
11
name = name.to_sym if name
-
11
if name == :none || name.nil?
-
5
@current = nil
-
5
Origen.app.session.origen_core[:default_plugin] = nil
-
5
Origen.app.session.origen_core[:default_plugin_cleared_by_user] = true
-
else
-
6
Origen.app.session.origen_core[:default_plugin] = name
-
6
@current = name
-
end
-
end
-
-
2
def temporary=(name)
-
80
name = name.to_sym if name
-
80
@temporary = name
-
end
-
-
# Temporarily set the current plugin to nil
-
2
def disable_current
-
2
@disabled = true
-
2
if block_given?
-
1
yield
-
1
@disabled = false
-
end
-
end
-
-
# Restore the current plugin after an earlier disable
-
2
def enable_current
-
1
@disabled = false
-
end
-
-
# @deprecated
-
2
def default=(name)
-
1
Origen.deprecated 'Origen.current_plugin.default= is deprecated, use Origen.app.plugins.current= instead'
-
1
self.current = name
-
end
-
-
# @deprecated
-
2
def name
-
2
Origen.deprecated 'Origen.current_plugin.name is deprecated, use Origen.app.plugins.current.name instead'
-
2
current.name if current
-
end
-
-
# @deprecated
-
2
def instance
-
Origen.deprecated 'Origen.current_plugin.instance is deprecated, use Origen.app.plugins.current instead'
-
current
-
end
-
-
# @deprecated
-
2
def default
-
Origen.deprecated 'Origen.current_plugin.default is deprecated, use Origen.app.plugins.current instead'
-
current
-
end
-
-
2
def shared_commands
-
[Origen.app, self].flatten.map do |plugin|
-
shared = plugin.config.shared || {}
-
if shared[:command_launcher]
-
"#{plugin.root}/#{shared[:command_launcher]}"
-
end
-
end.compact
-
end
-
-
# Return the plugin name if the path specified is from that plugin
-
2
def plugin_name_from_path(path)
-
27
path = Pathname.new(path).expand_path.cleanpath
-
27
each do |plugin|
-
173
if path.to_s =~ /^#{plugin.root}/
-
4
return plugin.name
-
end
-
end
-
nil
-
end
-
2
alias_method :path_within_a_plugin, :plugin_name_from_path
-
end
-
end
-
end
-
2
require 'fileutils'
-
2
module Origen
-
2
class Application
-
2
autoload :Statistics, 'origen/application/statistics'
-
-
# The Runner is responsible for co-ordinating all compile and generate
-
# requests from the command line
-
2
class Runner
-
2
attr_accessor :options
-
-
# Launch Origen, any command which generates an output file should launch from here
-
# as it gives a common point for listeners to hook in and to establish output
-
# directories and so on.
-
#
-
# Originally this method was called generate but that is now deprecated in favour
-
# of the more generic 'launch' as the Origen feature set has expanded.
-
2
def launch(options = {})
-
18
Origen.file_handler.preserve_state do
-
# Clean up the input from legacy code
-
18
options[:action] = extract_action(options)
-
18
options[:files] = extract_files(options)
-
18
@options = options
-
18
prepare_and_validate_workspace(options)
-
18
if options[:lsf]
-
record_invocation(options) do
-
prepare_for_lsf
-
Origen.app.listeners_for(:before_lsf_submission).each(&:before_lsf_submission)
-
batch = []
-
expand_lists_and_directories(options[:files], options).each do |file|
-
if options[:batch]
-
# Batch jobs into groups of 10
-
batch << file
-
if batch.size == options[:batch]
-
Origen.app.lsf_manager.submit_origen_job(batch.join(' '), options)
-
batch = []
-
end
-
else
-
Origen.app.lsf_manager.submit_origen_job(file, options)
-
end
-
end
-
if options[:batch]
-
Origen.app.lsf_manager.submit_origen_job(batch.join(' '), options) unless batch.empty?
-
end
-
end
-
Origen.log.info ''
-
Origen.log.info 'Monitor status of remote jobs via:'
-
Origen.log.info ' origen l'
-
else
-
18
unless tester && tester.try(:sim?)
-
18
Origen.log.info '*' * 70 unless options[:quiet]
-
end
-
18
Origen.app.listeners_for(:before_generate).each do |listener|
-
if listener.class.instance_method(:before_generate).arity == 0
-
listener.before_generate
-
else
-
listener.before_generate(options)
-
end
-
end
-
18
if Origen.running_remotely?
-
Origen.app.listeners_for(:before_generate_remote).each do |listener|
-
if listener.class.instance_method(:before_generate_remote).arity == 0
-
listener.before_generate_remote
-
else
-
listener.before_generate_remote(options)
-
end
-
end
-
else
-
18
Origen.app.listeners_for(:before_generate_local).each do |listener|
-
if listener.class.instance_method(:before_generate_local).arity == 0
-
listener.before_generate_local
-
else
-
listener.before_generate_local(options)
-
end
-
end
-
end
-
-
18
record_invocation(options) do
-
18
case options[:action]
-
when :forecast_test_time
-
Origen.time.forecast_test_time(options)
-
else
-
18
if options[:action] == :program
-
2
Origen.generator.generate_program(expand_lists_and_directories(options[:files], options), options)
-
2
Origen.app.listeners_for(:program_generated).each(&:program_generated)
-
else
-
16
temporary_plugin_from_options = options[:current_plugin]
-
16
if options[:action] == :pattern && options[:sequence]
-
1
patterns = expand_lists_and_directories(options[:files], options.merge(preserve_duplicates: true))
-
1
Origen.generator.generate_pattern(patterns, options)
-
1
Origen.app.plugins.temporary = nil if temporary_plugin_from_options
-
else
-
15
expand_lists_and_directories(options[:files], options).each do |file|
-
32
if temporary_plugin_from_options
-
30
Origen.app.plugins.temporary = temporary_plugin_from_options
-
end
-
32
case options[:action]
-
when :compile
-
7
Origen.generator.compile_file_or_directory(file, options)
-
when :merge
-
Origen.generator.merge_file_or_directory(file, options)
-
when :import_test_time
-
Origen.time.import_test_time(file, options)
-
when :import_test_flow
-
Origen.time.import_test_flow(file, options)
-
else
-
25
Origen.generator.generate_pattern(file, options)
-
end
-
32
Origen.app.plugins.temporary = nil if temporary_plugin_from_options
-
end
-
end
-
end
-
end
-
end
-
-
18
unless options[:quiet] || (tester && tester.try(:sim?))
-
18
Origen.log.info '*' * 70
-
18
stats.print_summary unless options[:action] == :merge
-
end
-
end
-
end
-
end
-
2
alias_method :generate, :launch
-
-
2
def prepare_and_validate_workspace(options = {})
-
164
confirm_production_ready(options)
-
164
prepare_directories(options)
-
end
-
-
# Post an invocation to the Origen server for usage statistics tracking.
-
#
-
# Posting an invocation was found to add ~0.5s to all command times,
-
# so here we run it in a separate thread to try and hide it behind
-
# the user's command.
-
#
-
# @api private
-
2
def record_invocation(options)
-
# record_invocation = false
-
# begin
-
# # Only record user invocations at this time, also bypass windows since it seems
-
# # that threads can't be trusted not to block
-
# unless Origen.running_remotely? # || Origen.running_on_windows?
-
# record_invocation = Thread.new do
-
# Origen.client.record_invocation(options[:action]) if options[:action]
-
# end
-
# end
-
# rescue
-
# # Don't allow this to kill an origen command
-
# end
-
18
yield
-
# begin
-
# unless Origen.running_remotely?
-
# # Wait for a server response, ideally would like to not wait here, but it seems if not
-
# # then invocation postings can be dropped, especially on windows
-
# Origen.profile 'waiting for recording invocation' do
-
# record_invocation.value
-
# end
-
# end
-
# rescue
-
# # Don't allow this to kill an origen command
-
# end
-
end
-
-
# The action to take should be set by the action option, but legacy code will pass
-
# things like :compile => true, the extract_action method handles the old code
-
2
def extract_action(options)
-
18
return options[:action] if options[:action]
-
14
if options[:compile]
-
6
:compile
-
8
elsif options[:program]
-
:program
-
8
elsif options[:job_type] == :merge
-
:merge
-
else
-
8
:pattern
-
end
-
end
-
-
# Legacy file references can be input via :pattern, :patterns, etc. this
-
# cleans it up and forces them all to be in an array assigned to options[:files]
-
2
def extract_files(options)
-
18
files = [options[:pattern]] + [options[:patterns]] + [options[:file]] + [options[:files]]
-
18
files.flatten!
-
18
files.compact!
-
18
files
-
end
-
-
2
def shutdown
-
2
if Origen.app.stats.failed_files > 0 ||
-
Origen.app.stats.failed_patterns > 0
-
exit 1
-
end
-
end
-
-
# Expands any list references in the supplied pattern array and
-
# returns an array of pattern names. No guarantee is made to
-
# whether the pattern names are valid at this stage.
-
# Any duplicates will be removed.
-
2
def expand_lists_and_directories(files, options = {})
-
18
Origen.file_handler.expand_list(files, options)
-
end
-
-
2
def statistics
-
2695
@statistics ||= Statistics.new(options)
-
end
-
2
alias_method :stats, :statistics
-
-
2
def prepare_for_lsf
-
if options[:lsf]
-
# Build an options string for saving with the LSF job that represents this runtime environment
-
str = "-t #{Origen.target.file.basename}"
-
if Origen.environment.file
-
str += " --environment #{Origen.environment.file.basename}"
-
end
-
if options[:output]
-
str += " -o #{options[:output]}"
-
end
-
if options[:reference]
-
str += " -r #{options[:reference]}"
-
end
-
options[:lsf_option_string] = str
-
# Clear the LSF manager job list if specifically requested or if that is the default action and
-
# no specific action has been requested
-
if options[:lsf_action]
-
if options[:lsf_action] == :clear
-
Origen.app.lsf_manager.clear_all
-
end
-
elsif Origen.config.default_lsf_action == :clear
-
Origen.app.lsf_manager.clear_all
-
end
-
end
-
end
-
-
2
def prepare_directories(options = {})
-
# When running remotely on the LSF never create directories to
-
# prevent race conditions as multiple processes run concurrently,
-
# instead assume they were already created by the runner who
-
# submitted the job.
-
164
Origen.file_handler.set_output_directory(options.merge(create: Origen.running_locally?))
-
164
Origen.file_handler.set_reference_directory(options.merge(create: Origen.running_locally?))
-
164
unless Origen.running_globally?
-
164
tmp = "#{Origen.root}/tmp"
-
164
FileUtils.mkdir(tmp) unless File.exist?(tmp)
-
164
if Origen.running_locally?
-
164
mkdir Origen::Log.log_file_directory
-
164
mkdir "#{Origen.root}/.lsf"
-
end
-
164
if options[:lsf]
-
mkdir Origen.app.lsf_manager.log_file_directory
-
end
-
end
-
end
-
-
# Make the given directory if it doesn't exist, must be a full path
-
2
def mkdir(dir)
-
328
unless File.exist?(dir)
-
FileUtils.mkdir_p(dir)
-
end
-
end
-
-
2
def confirm_production_ready(_options = {})
-
# The caller would have already verified the status before submission
-
164
if Origen.running_locally?
-
164
if Origen.mode.production? && Origen.app.rc
-
unless Origen.app.rc.local_modifications.empty?
-
puts <<-EOT
-
Your workspace is running in production mode and it has local modifications which are preventing
-
the requested action, run the following command to see what files have been modified:
-
-
origen rc mods
-
-
If you are currently developing this application and are not ready to check everything in yet,
-
then run the following command to switch your workspace to debug/development mode:
-
-
origen m debug
-
EOT
-
exit 1
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
class Application
-
# Responsible for keeping track of all stats collected during a run
-
2
class Statistics
-
2
attr_accessor :completed_files, :failed_files, :missing_files,
-
:new_files, :changed_files
-
2
attr_accessor :completed_patterns, :failed_patterns, :missing_patterns,
-
:new_patterns, :changed_patterns
-
2
attr_accessor :total_vectors, :total_cycles, :total_duration, :errors
-
-
2
class Pattern
-
2
attr_accessor :vectors, :cycles, :duration
-
2
def initialize
-
86
@vectors = 0
-
86
@cycles = 0
-
86
@duration = 0
-
end
-
end
-
-
2
def initialize(options)
-
2
@options = options
-
2
@patterns = {}
-
2
reset_global_stats
-
end
-
-
2
def reset_global_stats
-
2
@completed_files = 0
-
2
@failed_files = 0
-
2
@missing_files = 0
-
2
@new_files = 0
-
2
@changed_files = 0
-
-
2
@completed_patterns = 0
-
2
@failed_patterns = 0
-
2
@missing_patterns = 0
-
2
@new_patterns = 0
-
2
@changed_patterns = 0
-
-
2
@total_vectors = 0
-
2
@total_cycles = 0
-
2
@total_duration = 0
-
-
2
@errors = 0
-
end
-
-
2
def reset_pattern_stats
-
end
-
-
2
def print_summary
-
18
method = clean_run? ? :success : :info
-
18
if @completed_patterns > 0 || @failed_patterns > 0
-
10
Origen.log.send method, "Total patterns: #{@completed_patterns}"
-
10
Origen.log.send method, "Total vectors: #{@total_vectors}"
-
10
Origen.log.send method, 'Total duration: %.6f' % @total_duration
-
10
Origen.log.send method, "New patterns: #{@new_patterns}"
-
10
if @changed_patterns > 0
-
Origen.log.warn "Changed patterns: #{@changed_patterns}"
-
else
-
10
Origen.log.send method, "Changed patterns: #{@changed_patterns}"
-
end
-
10
Origen.log.error "FAILED patterns: #{@failed_patterns}" if @failed_patterns > 0
-
10
Origen.log.info
-
end
-
18
if @completed_files > 0 || @failed_files > 0
-
18
Origen.log.send method, "Total files: #{@completed_files}"
-
18
Origen.log.send method, "New files: #{@new_files}"
-
18
Origen.log.send method, "Changed files: #{@changed_files}"
-
18
Origen.log.error "FAILED files: #{@failed_files}" if @failed_files > 0
-
18
Origen.log.info
-
end
-
18
if @errors > 0
-
Origen.log.error "ERRORS: #{@errors}"
-
end
-
-
18
if @changed_files > 0 || @changed_patterns > 0
-
changes = true
-
Origen.log.info 'To accept all of these changes run:'
-
Origen.log.info ' origen save changed'
-
end
-
18
if @new_files > 0 || @new_patterns > 0
-
3
news = true
-
3
Origen.log.info 'To save all of these new files as the reference version run:'
-
3
Origen.log.info ' origen save new'
-
end
-
18
if changes && news
-
Origen.log.info 'To save both new and changed files run:'
-
Origen.log.info ' origen save all'
-
end
-
18
Origen.log.info '**********************************************************************'
-
end
-
-
2
def summary_text
-
<<-END
-
Total patterns: #{@completed_patterns}
-
New patterns: #{@new_patterns}
-
Changed patterns: #{@changed_patterns}
-
FAILED patterns: #{@failed_patterns}
-
-
Total files: #{@completed_files}
-
New files: #{@new_files}
-
Changed files: #{@changed_files}
-
FAILED files: #{@failed_files}
-
-
ERRORS: #{@errors}
-
END
-
end
-
-
2
def clean_run?
-
18
@changed_files == 0 && @changed_patterns == 0 &&
-
@new_files == 0 && @new_patterns == 0 &&
-
@failed_files == 0 && @failed_patterns == 0 &&
-
@errors == 0
-
end
-
-
2
def record_failed_pattern
-
@failed_patterns += 1
-
end
-
-
2
def record_missing_pattern
-
@missing_patterns += 1
-
end
-
-
2
def add_vector(x = 1)
-
2295
current_pattern.vectors += x
-
end
-
-
2
def add_cycle(x = 1)
-
2295
current_pattern.cycles += x
-
end
-
-
2
def add_time_in_ns(x)
-
2295
current_pattern.duration += x
-
end
-
-
2
def collect_for_pattern(key)
-
86
@pattern_key = key
-
86
yield
-
86
@pattern_key = nil
-
end
-
-
2
def current_pattern
-
6885
pattern(@pattern_key)
-
end
-
-
2
def pattern(key)
-
7319
@patterns[key] ||= Pattern.new
-
end
-
-
2
def number_of_vectors_for(key)
-
172
pattern(key).vectors
-
end
-
-
2
def number_of_cycles_for(key)
-
86
pattern(key).vectors
-
end
-
-
2
def execution_time_for(key)
-
176
pattern(key).duration.to_f / 1_000_000_000
-
end
-
-
2
def record_pattern_completion(key)
-
86
@completed_patterns += 1
-
86
@total_vectors += number_of_vectors_for(key)
-
86
@total_cycles += number_of_cycles_for(key)
-
86
@total_duration += execution_time_for(key)
-
end
-
-
2
def report_pass
-
2
Origen.log.success ''
-
2
Origen.log.success ' PPPPP AA SSSS SSSS'
-
2
Origen.log.success ' PP PP AAAA SS SS SS SS'
-
2
Origen.log.success ' PPPPP AA AA SS SS'
-
2
Origen.log.success ' PP AAAAAAAA SS SS'
-
2
Origen.log.success ' PP AA AA SS SS SS SS'
-
2
Origen.log.success ' PP AA AA SSSS SSSS'
-
2
Origen.log.success ''
-
end
-
-
2
def report_fail
-
Origen.log.error ''
-
Origen.log.error ' FFFFFF AA II LL'
-
Origen.log.error ' FF AAAA II LL'
-
Origen.log.error ' FFFFF AA AA II LL'
-
Origen.log.error ' FF AAAAAAAA II LL'
-
Origen.log.error ' FF AA AA II LL'
-
Origen.log.error ' FF AA AA II LLLLLL'
-
Origen.log.error ''
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
class Application
-
# Class to handle the target.
-
#
-
# The target is a Ruby file that is run prior to generating each pattern, and
-
# it should be used to instantiate the top-level models used by the application.
-
# It can also be used to override and settings within these classes after they
-
# have been instantiated and before they are run.
-
#
-
# All target files must live in Origen.root/target.
-
#
-
# An instance of this class is automatically instantiated and available globally
-
# as Origen.app.target
-
2
class Target
-
2
DIR = "#{Origen.root}/target" # :nodoc:
-
2
SAVE_FILE = "#{DIR}/.default" # :nodoc:
-
2
DEFAULT_FILE = "#{DIR}/default.rb" # :nodoc:
-
-
# Not a clean unload, but allows objects to be re-instantiated for testing
-
# @api private
-
2
def unload!
-
Origen.app.unload_target!
-
end
-
-
# Implement a target loop based on the supplied options.
-
# The options can contain the keys :target or :targets or neither.
-
#
-
# In the neither case the loop will run once for the current workspace
-
# default target.
-
#
-
# In the case where one of the keys is present the loop will run for each
-
# target and the options will be passed into the block with the key :target
-
# set to the current target.
-
2
def loop(options = {})
-
options = {
-
set_target: true,
-
force_debug: false, # Set true to force debug mode for all targets
-
}.merge(options)
-
targets = [options.delete(:target), options.delete(:targets)].flatten.compact.uniq
-
targets = [file!.basename.to_s] if targets.empty?
-
set = options.delete(:set_target)
-
targets.each do |target|
-
Origen.load_target(target, options) if set
-
options[:target] = target
-
yield options
-
end
-
end
-
-
# Use this to implement a loop for each production target, it will automatically
-
# load each target before yielding to the block.
-
#
-
# The production targets are defined by the production_targets configuration
-
# option.
-
# === Example
-
# Origen.app.target.each_production do
-
# Run something within the context of each target
-
# end
-
2
def each_production(options = {})
-
options = {
-
force_debug: false, # Set true to force debug mode for all targets
-
}.merge(options)
-
prod_targets.each do |moo, targets|
-
[targets].flatten.each do |target|
-
self.temporary = target
-
Origen.app.load_target!(options)
-
yield moo
-
end
-
end
-
end
-
-
# As each_production except it only yields unique targets. i.e. if you have two
-
# MOOs that use the same target file defined in the production_targets then this
-
# method will only yield once.
-
#
-
# An array of MOOs that use each target is returned each time.
-
# === Example
-
# Origen.app.target.each_unique_production do |moos|
-
# Run something within the context of each unique target
-
# end
-
2
def each_unique_production(options = {})
-
options = {
-
force_debug: false, # Set true to force debug mode for all targets
-
}.merge(options)
-
targets = {}
-
prod_targets.each do |moo, moos_targets|
-
[moos_targets].flatten.each do |target|
-
if targets[target]
-
targets[target] << moo
-
else
-
targets[target] = [moo]
-
end
-
end
-
end
-
targets.each do |target, moos|
-
self.temporary = target
-
Origen.app.load_target!(options)
-
yield moos
-
end
-
end
-
-
# If the production_targets moo number mapping inclues the current target then
-
# the MOO number will be returned, otherwise nil
-
2
def moo
-
3
prod_targets.each do |moo, targets|
-
6
[targets].flatten.each do |target|
-
6
return moo if File.basename(target, '.rb').to_s == file.basename('.rb').to_s
-
end
-
end
-
nil
-
end
-
-
# Returns the name (the filename) of the current target
-
2
def name
-
438
file.basename('.rb').to_s if file
-
end
-
-
# Load the target, calling this will re-instantiate all top-level objects
-
# defined there.
-
2
def load!(options = {})
-
options = {
-
89
force_debug: false, # Set true to force debug mode for all targets
-
}.merge(options)
-
89
Origen.app.load_target!(options)
-
end
-
-
# Returns Array of all targets available
-
2
def all_targets
-
6
targets = []
-
6
find('').sort.each do |file|
-
102
targets << File.basename(file)
-
end
-
6
targets # return
-
end
-
-
# Returns an array containing all current production targets
-
2
def production_targets
-
prod_targets.map { |_moo, targets| targets }.uniq
-
end
-
-
# Returns true if the target exists, this can be used to test for the presence
-
# of a target before calling one of the other methods to actually apply it.
-
#
-
# It will return true if one or more targets are found matching the given name,
-
# use the unique? method to test if the given name uniquely identifies a valid
-
# target.
-
2
def exists?(name)
-
7
tgts = resolve_mapping(name)
-
7
targets = tgts.is_a?(Array) ? tgts : find(tgts)
-
7
targets.size > 0
-
end
-
2
alias_method :exist?, :exists?
-
-
# Similar to the exists? method, this will return true only if the given name
-
# resolves to a single valid target.
-
2
def unique?(name)
-
2
tgts = resolve_mapping(name)
-
2
targets = tgts.is_a?(Array) ? tgts : find(tgts)
-
2
targets.size == 1
-
end
-
-
# Switch to the supplied target, name can be a fragment as long as it allows
-
# a unique target to be identified.
-
#
-
# The name can also be a MOO number mapping from the config.production_targets
-
# attribute of the application.
-
#
-
# Calling this method does not affect the default target setting in the workspace.
-
#
-
# The target can also be set to a proc to be called instead, this is really
-
# intended to be used for testing purposes:
-
# Origen.target.temporary = -> { $dut = SomeLocalClass.new }
-
2
def temporary=(name)
-
259
if name.is_a?(Proc)
-
64
self.file = nil
-
64
@proc = name
-
64
return
-
else
-
195
@proc = nil
-
end
-
195
tgts = resolve_mapping(name)
-
195
targets = tgts.is_a?(Array) ? tgts : find(tgts)
-
195
if targets.size == 0
-
2
puts "Sorry no targets were found matching '#{name}'!"
-
2
puts 'Here are the available options:'
-
2
find('').sort.each do |file|
-
34
puts File.basename(file)
-
end
-
2
exit 1
-
193
elsif targets.size > 1
-
if is_a_moo_number?(name) && prod_targets
-
puts "Multiple production targets exist for #{name.upcase}, use one of the following instead of the MOO number:"
-
targets.sort.each do |file|
-
puts File.basename(file)
-
end
-
else
-
puts 'Please try again with one of the following targets:'
-
targets.sort.each do |file|
-
puts File.basename(file)
-
end
-
end
-
exit 1
-
else
-
193
self.file = targets[0]
-
end
-
end
-
2
alias_method :switch, :temporary=
-
2
alias_method :switch_to, :temporary=
-
-
2
def proc
-
456
@proc
-
end
-
-
# Returns a signature for the current target, can be used to track target
-
# changes in cases where the name is not unique - i.e. when using a
-
# configurable target
-
2
def signature
-
31
@signature ||= set_signature(nil)
-
end
-
-
# @api private
-
2
def set_signature(options)
-
392
options ||= {}
-
392
@signature = options.merge(_tname: name).to_a.hash
-
end
-
-
# As #temporary= except that the given target will be set to the workspace default
-
2
def default=(name)
-
1
if name
-
1
self.temporary = name
-
else
-
@file = nil
-
end
-
1
save
-
end
-
-
# Prints out the current target details to the command line
-
2
def describe
-
f = self.file!
-
puts "Current target: #{f.basename}"
-
puts '*' * 70
-
File.open(f).each do |line|
-
puts " #{line}"
-
end
-
puts '*' * 70
-
end
-
-
# Returns an array of matching target file paths
-
2
def find(name)
-
212
if name
-
211
name = name.gsub('*', '')
-
211
if File.exist?(name)
-
[name]
-
211
elsif File.exist?("#{Origen.root}/target/#{name}") && name != ''
-
2
["#{Origen.root}/target/#{name}"]
-
else
-
# The below weirdness is to make it recurse into symlinked directories
-
209
Dir.glob("#{DIR}/**{,/*/**}/*").sort.uniq.select do |file|
-
3554
File.basename(file) =~ /#{name}/ && file !~ /.*\.rb.+$/
-
end
-
end
-
else
-
1
[nil]
-
end
-
end
-
-
# Saves the current target as the workspace default, i.e. the current target
-
# will be used by Origen the next time if no other target is specified
-
2
def save # :nodoc:
-
1
if @file
-
1
File.open(SAVE_FILE, 'w') do |f|
-
1
Marshal.dump(file, f)
-
end
-
else
-
forget
-
end
-
end
-
-
# Load the default file from the workspace default if it exists and return it,
-
# otherwise returns nil
-
2
def default_file
-
183
return @default_file if @default_file
-
1
if File.exist?(SAVE_FILE)
-
1
File.open(SAVE_FILE) do |f|
-
1
@default_file = Marshal.load(f)
-
end
-
elsif File.exist?(DEFAULT_FILE)
-
@default_file = Pathname.new(DEFAULT_FILE)
-
end
-
1
@default_file
-
end
-
-
# Returns the target file (a Pathname object) if it has been defined, otherwise nil
-
2
def file # :nodoc:
-
1632
return @file if @file
-
61
if default_file && File.exist?(default_file)
-
61
@file = default_file
-
end
-
end
-
-
# As file except will raise an exception if it hasn't been defined yet
-
2
def file! # :nodoc:
-
328
unless file
-
puts 'No target has been specified!'
-
puts 'To specify a target use the -t switch.'
-
puts 'Look in the target directory for a list of available target names.'
-
exit 1
-
end
-
328
file
-
end
-
-
2
def file=(path) # :nodoc:
-
257
if path
-
192
@file = Pathname.new(path)
-
else
-
65
@file = nil
-
end
-
end
-
-
# Remove the workspace default target
-
2
def forget
-
File.delete(SAVE_FILE) if File.exist?(SAVE_FILE)
-
@default_file = nil
-
end
-
-
# # This attribute is used by the Origen#compile and Origen#merge tasks to allow files
-
# # to be compiled on a per-target basis. If the ERB source file has 'target' in the
-
# # name then this will be substituted for the value returned from this attribute. <br>
-
# # For example to simply use the MOO number to identify the target you may
-
# # set up a simple task like this:
-
# # # Compile the J750 files for each target
-
# # Target.each_production do
-
# # $target.id = $target.moo.gsub("*","")
-
# # compile("templates/j750", "j750")
-
# # end
-
# attr_accessor :id
-
#
-
# def initialize # :nodoc:
-
# restore
-
# end
-
#
-
# # Yields a summary of the current target settings
-
# def summary
-
# yield "Top: #{$top.class}"
-
# yield "SoC: #{$soc.class}"
-
# yield "Tester: #{$tester.class}"
-
# end
-
-
# Returns true if running with a temporary target, i.e. if the current
-
# target is not the same as the default target
-
2
def temporary?
-
@file == @default_file
-
end
-
-
# Resolves the target name to a target file if a MOO number is supplied and
-
# app.config.production_targets has been defined
-
2
def resolve_mapping(name) # :nodoc:
-
204
if is_a_moo_number?(name) && prod_targets
-
# If an exact match
-
10
if prod_targets[name.upcase]
-
5
prod_targets[name.upcase]
-
# If a wildcard match
-
5
elsif prod_targets["*#{moo_number_minus_revision(name)}"]
-
prod_targets["*#{moo_number_minus_revision(name)}"]
-
# Else just return the given name
-
else
-
5
name
-
end
-
else
-
194
name
-
end
-
end
-
-
# Returns config.production_targets with all keys forced to upper case
-
2
def prod_targets # :nodoc:
-
33
return {} unless Origen.config.production_targets
-
33
return @prod_targets if @prod_targets
-
2
@prod_targets = {}
-
2
Origen.config.production_targets.each do |key, value|
-
6
@prod_targets[key.upcase] = value
-
end
-
2
@prod_targets
-
end
-
-
# Returns true if the supplied target name is a moo number format
-
2
def is_a_moo_number?(name) # :nodoc:
-
204
!!(name.to_s.upcase =~ /^\d?\d?\*?[A-Z]\d\d[A-Z]$/)
-
end
-
-
2
def moo_number_minus_revision(name) # :nodoc:
-
5
name.to_s.upcase =~ /^\d?\d?([A-Z]\d\d[A-Z])$/
-
5
Regexp.last_match[1]
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
class Application
-
2
class WorkspaceManager
-
# Returns the directory that contains the current application's revision control
-
# root (basically just Origen.app.rc.root.parent)
-
2
def container_directory
-
4
if Origen.running_on_windows?
-
dir = revision_control_root.parent
-
Pathname.new(dir.to_s.sub(/\/$/, ''))
-
else
-
4
revision_control_root.parent
-
end
-
end
-
-
# Returns the path to the root directory of the revision control system
-
# that is managing the application.
-
#
-
# This may not necessarily be Origen.root if the application is embedded within
-
# a larger project workspace (for example in tool_data/origen)
-
2
def revision_control_root
-
8
Origen.app.rc ? Origen.app.rc.root : Origen.root
-
end
-
-
# Origen.root may not necessarily be the same as the revision control root.
-
# This method will return the relative path from the revision control root to
-
# Origen.root.
-
2
def path_to_origen_root
-
path = Origen.root.to_s.sub(revision_control_root.to_s, '').sub(/^(\/|\\)/, '')
-
path = '.' if path.empty?
-
path
-
end
-
-
# Provides a proposal for where the reference workspace should live
-
2
def reference_workspace_proposal
-
"#{container_directory}/#{Origen.app.name}_reference"
-
end
-
-
# Returns the path to the actual reference workspace if it is set,
-
# otherwise returns nil
-
2
def reference_workspace
-
if reference_workspace_set?
-
dir = File.readlink(reference_dir)
-
dir.gsub!('.ref', '')
-
dir.gsub!(/#{Regexp.escape(path_to_origen_root)}\/?$/, '')
-
Pathname.new(dir).cleanpath
-
end
-
end
-
-
# Returns the path to the directory that will be used to contain
-
# all imported application workspaces
-
2
def imports_directory
-
return @imports_directory if @imports_directory
-
old = "#{container_directory}/#{revision_control_root.basename}_imports_DO_NOT_HAND_MODIFY"
-
@imports_directory = "#{container_directory}/.#{revision_control_root.basename}_imports_DO_NOT_HAND_MODIFY"
-
FileUtils.rm_rf(old) if File.exist?(old)
-
@imports_directory
-
end
-
-
# Returns the path to the directory that will be used to contain
-
# all remotes workspaces
-
2
def remotes_directory
-
4
return @remotes_directory if @remotes_directory
-
2
old = "#{container_directory}/#{revision_control_root.basename}_remotes_DO_NOT_HAND_MODIFY"
-
2
@remotes_directory = "#{container_directory}/.#{revision_control_root.basename}_remotes_DO_NOT_HAND_MODIFY"
-
2
FileUtils.rm_rf(old) if File.exist?(old)
-
2
@remotes_directory
-
end
-
-
# Returns true if the local reference directory is already
-
# pointing to an external workspace.
-
2
def reference_workspace_set?
-
f = reference_dir
-
File.exist?(f) && File.symlink?(f) &&
-
File.exist?(File.readlink(f))
-
end
-
-
2
def set_reference_workspace(workspace)
-
f = reference_dir
-
if File.exist?(f)
-
if File.symlink?(f)
-
FileUtils.rm_f(f)
-
else
-
FileUtils.rm_rf(f)
-
end
-
end
-
remote_ref = "#{origen_root(workspace)}/.ref"
-
unless File.exist?(remote_ref)
-
FileUtils.mkdir_p(remote_ref)
-
`touch #{remote_ref}/dont_delete` # Make sure the pop does not blow this away
-
end
-
if Origen.running_on_windows?
-
system("call mklink /h #{reference_dir} #{remote_ref}")
-
else
-
File.symlink(remote_ref, reference_dir)
-
end
-
end
-
-
# Returns the full path to Origen.root within the given workspace
-
2
def origen_root(workspace)
-
Pathname.new("#{workspace}/#{path_to_origen_root}").cleanpath
-
end
-
-
2
def reference_dir
-
"#{Origen.root}/.ref" # Should probably be set by a config parameter
-
end
-
-
# Builds a new workspace at the given path
-
2
def build(path, options = {})
-
options = {
-
rc_url: Origen.app.config.rc_url || Origen.app.config.vault,
-
allow_rebuild: false
-
}.merge(options)
-
if File.exist?(path.to_s) && !options[:allow_rebuild]
-
fail "Sorry but #{path} already exists!"
-
end
-
FileUtils.rm_rf(path.to_s) if File.exist?(path.to_s)
-
rc = RevisionControl.new remote: options[:rc_url], local: path.to_s
-
rc.build
-
end
-
-
# Switches the given workspace path to the given version tag
-
2
def switch_version(workspace, tag, options = {})
-
options = {
-
origen_root_only: false, # When true pop the Origen.root dir only instead
-
# of the whole application workspace - these may or may
-
# not be the same thing depending on the application.
-
}.merge(options)
-
version_file = "#{workspace}/.current_version"
-
FileUtils.rm_f(version_file) if File.exist?(version_file)
-
if options[:origen_root_only]
-
dir = "#{workspace}/#{path_to_origen_root}"
-
else
-
dir = workspace
-
end
-
rc_url = Origen.app.config.rc_url || Origen.app.config.vault
-
rc = RevisionControl.new remote: rc_url, local: dir.to_s
-
rc.checkout version: tag, force: true
-
File.open(version_file, 'w') do |f|
-
f.puts tag
-
end
-
end
-
-
2
def current_version_of(workspace)
-
2
f = "#{workspace}/.current_version"
-
2
if File.exist?(f)
-
2
File.readlines(f).first.strip
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Bugs
-
2
class Bug
-
2
attr_reader :affected_versions
-
-
2
attr_reader :name
-
2
alias_method :id, :name
-
-
2
def initialize(name, options = {})
-
4
@name = name
-
4
@affected_versions = [options[:affected_version] || options[:affected_versions]].flatten.compact
-
4
@fixed_on_version = options[:fixed_on_version]
-
end
-
-
2
def present_on_version?(version, _options = {})
-
12
if affected_versions.empty?
-
6
if fixed_on_version
-
3
version < fixed_on_version
-
else
-
3
true
-
end
-
else
-
6
affected_versions.include?(version)
-
end
-
end
-
-
2
def fixed_on_version
-
12
@fixed_on_version || begin
-
5
unless affected_versions.empty?
-
1
affected_versions.max + 1
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Origen
-
# Represents an SoC DFT/Operating mode - e.g. SCAN, RAMBIST, etc.
-
1
class ChipMode
-
1
attr_accessor :brief_description
-
1
attr_accessor :description
-
1
attr_writer :name
-
1
attr_accessor :data_rate
-
1
attr_accessor :data_rate_unit
-
1
attr_accessor :minimum_version_enabled
-
1
alias_writer :min_ver_enabled, :minimum_version_enabled
-
1
alias_writer :min_version_enabled, :minimum_version_enabled
-
1
attr_accessor :audience
-
1
alias_writer :full_name, :name
-
# Returns the object that owns the mode (the SoC instance usually)
-
1
attr_accessor :owner
-
1
attr_accessor :typical_voltage
-
1
alias_method :typ_voltage, :typical_voltage
-
-
1
def initialize(name, options = {})
-
426
options.each { |k, v| instance_variable_set("@#{k}", v) }
-
382
(block.arity < 1 ? (instance_eval(&block)) : block.call(self)) if block_given?
-
382
@name = name
-
382
validate_args
-
end
-
-
1
def name
-
842
@name || @id
-
end
-
1
alias_method :full_name, :name
-
-
1
def id
-
805
@id || name.to_s.downcase.gsub(/(\s|-)+/, '_').to_sym
-
end
-
-
1
def id=(val)
-
@id = val.to_s.gsub(/(\s|-)+/, '_').downcase.to_sym
-
end
-
-
1
def data_rate(options = {})
-
options = {
-
3
absolute_number: true
-
}.update(options)
-
# Convert the data rate to a number
-
3
if !!@data_rate && !!@data_rate_unit
-
2
if options[:absolute_number]
-
# The data rate unit was validated on init so it is good to go
-
# in theory but should still check if it returns a numeric
-
1
value = @data_rate.send(@data_rate_unit.to_sym)
-
1
if value.is_a?(Numeric)
-
1
return value
-
else
-
Origen.log.error "@data_rate '#{@data_rate}' conversion using @data_rate_unit '#{@data_rate_unit}' did not product a Numeric, exiting..."
-
end
-
else
-
1
return @data_rate
-
end
-
else
-
1
return @data_rate
-
end
-
end
-
-
1
def respond_to_missing?(method_name, _include_private = false)
-
method_name[-1] == '?'
-
end
-
-
# Implements methods like:
-
#
-
# if $dut.mode.rambist?
-
1
def method_missing(method_name, *arguments, &block)
-
20
ivar = "@#{method_name.to_s.gsub('=', '')}"
-
20
ivar_sym = ":#{ivar}"
-
20
if method_name[-1] == '?'
-
return id == method_name[0..-2].to_sym
-
20
elsif method_name[-1] == '='
-
16
define_singleton_method(method_name) do |val|
-
16
instance_variable_set(ivar, val)
-
end
-
4
elsif instance_variables.include? ivar_sym
-
instance_variable_get(ivar)
-
else
-
4
define_singleton_method(method_name) do
-
4
instance_variable_get(ivar)
-
end
-
end
-
20
send(method_name, *arguments, &block)
-
end
-
-
1
def to_s
-
id.to_s
-
end
-
-
1
def to_sym
-
to_s.to_sym
-
end
-
-
1
private
-
-
1
def validate_args
-
382
unless @data_rate_unit.nil? || @data_rate_unit =~ /n\/a/i || @data_rate_unit =~ /na/i
-
# Remove special chars
-
4
['/', '-'].each do |special_char|
-
8
@data_rate_unit.gsub!(special_char, '')
-
end
-
# Check if @data_rate_unit is found in the Numeric core_ext lib
-
4
fail "@data_rate_unit '#{@data_rate_unit}' is not an accepted unit" unless 1.send(@data_rate_unit.to_sym)
-
# Cannot use @data_rate_unit without @data_rate
-
4
fail '@data_rate_unit must be set with @data_rate, exiting...' if @data_rate.nil?
-
end
-
382
unless @data_rate.nil? || @data_rate =~ /n\/a/i || @data_rate =~ /na/i
-
# Check if the data rate was passed as a String, if so convert it to a number
-
8
if @data_rate.is_a? String
-
4
if @data_rate.numeric?
-
4
@data_rate = @data_rate.to_numeric
-
else
-
fail "@data_rate '#{@data_rate}' cannot be converted to a number, exiting..."
-
end
-
end
-
end
-
382
unless @typical_voltage.nil?
-
if @typical_voltage.is_a? String
-
if @typical_voltage.numeric?
-
@typical_voltage = @typical_voltage.to_numeric
-
else
-
fail "@typical_voltage '#{@typical_voltage}' cannot be converted to a number, exiting..."
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Origen
-
# Represents an SoC Package option
-
1
class ChipPackage
-
1
require 'colored'
-
1
attr_accessor :description
-
1
attr_accessor :number_of_rows
-
1
attr_accessor :number_of_columns
-
1
attr_accessor :interconnects
-
1
attr_accessor :types
-
1
attr_reader :upper_axes
-
1
attr_reader :lower_axes
-
1
attr_reader :rows
-
1
attr_reader :columns
-
1
attr_reader :field
-
1
attr_reader :obj
-
1
attr_reader :groups
-
1
attr_reader :group_list
-
1
attr_reader :last_empty_char
-
1
attr_reader :plottable
-
1
attr_writer :name
-
1
alias_writer :full_name, :name
-
# Returns the owner that $owns the mode (the SoC instance usually)
-
1
attr_accessor :owner
-
-
1
def name
-
@name || @id
-
end
-
1
alias_method :full_name, :name
-
-
1
def id
-
588
@id || name.to_s.downcase.gsub(/(\s|-)+/, '_').to_sym
-
end
-
-
1
def id=(val)
-
199
@id = val.to_s.gsub(/(\s|-)+/, '_').downcase.to_sym
-
end
-
-
1
def to_s
-
id.to_s
-
end
-
-
1
def to_sym
-
to_s.to_sym
-
end
-
-
1
def types
-
@types || []
-
end
-
-
# prepare_plot should not need to be called explicitly. It is called
-
# by other methods when need-be, and is fundamentallyis responsible
-
# for two things:
-
#
-
# 1. It checks that self.types includes a "BGA" option.
-
# 2. It populates the .field attribute with a multi-dimensional
-
# array, proportional in size to the package BGA.
-
#
-
1
def prepare_plot
-
@plottable = types.any? { |x| /BGA/i =~ x }
-
if @plottable
-
@last_empty_char = '.'
-
@field = []
-
@groups = []
-
@group_list = list_groups
-
@columns = []
-
jedec_rows = %w(A B C D E F G H J K L M N P R T U V W Y AA AB AC AD AE AF AG AH AJ AK AL AM AN AP AR AT AU AV AW AY BA BB BC BD BE BF BG BH BJ BK BL BM BN BP)
-
@number_of_rows.times { @field.insert(0, []); @number_of_columns.times { @field[0].insert(0, []) } }
-
@rows = jedec_rows[0, @number_of_rows]
-
(1..@number_of_columns).map { |item| @columns << item }
-
@upper_axes = []
-
@lower_axes = []
-
@columns.each_with_index do|column, index|
-
# if index % 2 == 0
-
if index.even?
-
temp = column.to_s
-
temp += ' ' unless temp.size > 1
-
@upper_axes << temp
-
@lower_axes << ' '
-
else
-
temp = column.to_s
-
temp += ' ' unless temp.size > 1
-
@lower_axes << temp
-
@upper_axes << ' '
-
end
-
end
-
else
-
puts 'Sorry, currently the plot feature only supports BGA package types.'
-
end
-
end
-
-
# generate_field should not need to be called explicitly. It is called
-
# by other methods when need-be, and is fundamentallyis responsible
-
# for two things:
-
#
-
# 1. It fills the .field array with the appropriate symbols/markers.
-
# 2. It concatenates the array elements into printable rows, and prints
-
# them.
-
#
-
1
def generate_field(emptyChar = @last_empty_char)
-
if plottable
-
new_field = []
-
@field.each do |rows|
-
rows.each do |items|
-
if items.length == 0
-
items.insert(0, "#{emptyChar} ")
-
elsif emptyChar != @last_empty_char && items == ["#{last_empty_char} "]
-
items[0] = "#{emptyChar} "
-
end
-
end
-
new_field.insert(-1, rows.join(''))
-
end
-
@last_empty_char = emptyChar
-
package = (owner.package.nil?) ? 'No package chosen.' : owner.package.to_s
-
puts "\nPin field: #{package}\n\n"
-
group_display = @groups.join("\n")
-
puts "Legend: \n#{group_display}\n\n"
-
puts @upper_axes.join('').yellow
-
new_field.each_with_index { |line, index| puts line + "#{@rows[index]} (#{index + 1})\n".chop.yellow }
-
puts @lower_axes.join('').yellow, "\n"
-
end
-
end
-
1
alias_method :show, :generate_field
-
-
1
def add_power(marker = 'P')
-
# add_power can be called explicitly or by the .plot("power") method call.
-
prepare_plot if field.nil?
-
if plottable
-
pin_list = owner.power_pins.map { |_ken, pin| pin }
-
@groups << "#{marker} - Power"
-
pin_list.each do |item|
-
# puts items,owner.pins[items].location
-
begin
-
coordinates = coordinate(item.location)
-
@field[coordinates[0]][coordinates[1]] = [marker.red + ' ']
-
rescue
-
puts "#{item} doesn't appear to have a physical location in this configuration."
-
puts "Current package = #{owner.package}"
-
end
-
end
-
generate_field
-
end
-
end
-
1
alias_method :add_powers, :add_power
-
1
alias_method :plot_power, :add_power
-
1
alias_method :plot_powers, :add_power
-
-
1
def add_grounds(marker = 'G')
-
# add_grounds can be called explicitly, or by the .plot("grounds") method call.
-
prepare_plot if field.nil?
-
if plottable
-
pin_list = owner.ground_pins.map { |_ken, pin| pin }
-
@groups << "#{marker} - Ground"
-
pin_list.each do |item|
-
begin
-
coordinates = coordinate(item.location)
-
@field[coordinates[0]][coordinates[1]] = [marker.green + ' ']
-
rescue
-
puts "#{item} doesn't appear to have a physical location in this configuration."
-
end
-
end
-
generate_field
-
end
-
end
-
1
alias_method :add_ground, :add_grounds
-
1
alias_method :plot_ground, :add_grounds
-
1
alias_method :plot_grounds, :add_grounds
-
-
1
def clear
-
# clear removes all elements from the .field and .groups attributes.
-
prepare_plot if field.nil?
-
if plottable
-
@groups = []
-
@field = []
-
@number_of_rows.times { @field.insert(0, []); @number_of_columns.times { @field[0].insert(0, []) } }
-
end
-
end
-
-
1
def plot_help
-
prepare_plot if field.nil?
-
puts "\n#################### PLOT HELP ####################"
-
puts 'To generate in-console BGA plots, the ChipPackage class will respond to the following methods:'
-
puts '.list_groups, .plot(), .plot_coord(), .show(), and .clear'
-
if plottable
-
puts "\nPLOTTING GROUPS:"
-
puts '$dut.package.list_groups <-- to see available group names'
-
puts "$dut.package.plot(\"ddr_interface_1\")"
-
puts "$dut.package.plot_group(\"serdes_1\",'Z') <--denotes custom legend marker, Z"
-
puts "\nPLOTTING INDIVIDUAL PINS:"
-
puts "$dut.package.plot(\"d1_mdqs00\")"
-
puts "\nPLOTTING WITH REGEXP:"
-
puts "$dut.package.plot(\"d1_mdqs\") <-- Plot all controller 1 DQS pins."
-
puts "$dut.package.plot(\"d1_mdqs0[0-9]\") <-- Plot d1_mdqs00 - d1_mdqs09."
-
puts "\nADDING POWER/GROUND:"
-
puts "$dut.package.plot(\"grounds\")"
-
puts "$dut.package.plot(\"power\")"
-
puts "\nVIEW CURRENT PLOT\n"
-
puts '$dut.package.show'
-
else
-
puts 'Currently, only BGA package types are supported for in-console plotting.'
-
end
-
end
-
1
alias_method :help_plot, :plot_help
-
-
1
def list_groups
-
# returns an array of group names assigned to the package
-
grps = owner.pins.map { |_key, val| val.group }
-
grps.uniq!
-
rescue
-
return []
-
end
-
1
alias_method :group_list, :list_groups
-
-
1
def group_array(grp)
-
# returns an array of pins belonging to the given group
-
pin_list = $dut.pins.map { |_key, val| val if val.group == grp }
-
pin_list[0].compact!
-
puts 'No pins found under that group name.' unless pin_list.any?
-
rescue
-
return []
-
end
-
-
# ##############################################################
-
# ############# String/Coordinate Manipulation #################
-
# ##############################################################
-
-
1
def coordinate(location)
-
## Returns array of numerical equiv coordinates (e.g. "AA11" -> [20,11])
-
error = "\n\nSomething wrong during coordinate-mapping.\nAre you sure you passed an alphanumeric string\n to the coordinate() method?\n"
-
split_index = -1
-
location.each_char do |character|
-
if letter?(character)
-
split_index += 1
-
end
-
end
-
row = location[0..split_index]
-
column = location[split_index + 1..-1]
-
fail ArgumentError, error unless row.length > 0 && column.length > 0
-
## Now convert alphanumeric row to jedec equiv' with to_row() method
-
# and return coordinates.
-
coordinates = [to_row(row), column.to_i - 1]
-
end
-
-
1
def to_row(alphanumeric_coord)
-
## This maps alpha row coordinate to its appropriate Jedec:
-
number = alphanumeric_coord.upcase.tr('A-HJ-NPRT-WY', '1-9a-q').to_i(21) - 1
-
number -= (number / 21)
-
end
-
-
1
def letter?(test_character)
-
## Returns nil if the 'test_character' isn't a letter.
-
test_character =~ /[[:alpha:]]/
-
end
-
-
1
def initial(test_string)
-
test_string[0, 1]
-
end
-
-
# .plot can be called explicitly and accepts string arguments with
-
# or without regex styling. For example:
-
#
-
# $dut.package = :t4240
-
# $dut.package.plot("ddr_interface") # plots all "ddr interface" groups
-
# $dut.package.plot("grounds") # adds ground pins to the previously instantiated plot
-
# $dut.package.plot("d1_mdq37","$") # plots controller 1 mdq 37, and uses $ as a legend marker
-
# $dut.package.plot("d2_mdq[3-6]0) # plots d2_mdq30, d2_mdq40, d2_md520, and d2_md620
-
#
-
1
def plot(pinName, marker = nil)
-
prepare_plot if field.nil?
-
if plottable && pinName.is_a?(String) && /ground/ =~ pinName.downcase
-
add_grounds('G')
-
elsif plottable && pinName.is_a?(String) && /power/ =~ pinName.downcase
-
add_power('P')
-
elsif plottable && pinName.is_a?(String)
-
found_pins = []
-
owner.pins.map { |pin| found_pins << pin[1] if /#{pinName}/ =~ pin[1].name.to_s || /#{pinName}/ =~ pin[1].group.to_s }
-
if found_pins.size == 1 && marker.nil?
-
marker = initial(pinName.to_s)
-
while @groups.index { |grpName| grpName =~ /#{marker} -/ }
-
marker.next!
-
marker = '0' unless marker.size < 2
-
end
-
coordinates = coordinate(found_pins[0].location)
-
@field[coordinates[0]][coordinates[1]] = [marker.white_on_blue + ' ']
-
@groups.delete_if { |group| /#{pinName.to_s}/ =~ group }
-
@groups << "#{marker} - #{found_pins[0].name} - #{found_pins[0].location}"
-
elsif found_pins.size == 1
-
coordinates = coordinate(found_pins[0].location)
-
@field[coordinates[0]][coordinates[1]] = [marker.white_on_blue + ' ']
-
@groups.delete_if { |group| /#{pinName.to_s}/ =~ group }
-
@groups << "#{marker} - #{found_pins[0].name} - #{found_pins[0].location}"
-
else
-
if marker.nil?
-
marker = initial(pinName.to_s)
-
while @groups.index { |grpName| grpName =~ /#{marker} -/ }
-
marker.next!
-
marker = '0' unless marker.size < 2
-
end
-
end
-
reg_state = quote_regex(pinName)
-
found_pins.each do |item|
-
begin
-
coordinates = coordinate(item.location)
-
@field[coordinates[0]][coordinates[1]] = [marker + ' ']
-
@groups.delete_if { |group| "#{marker} - \"#{reg_state}\"" == group }
-
@groups << "#{marker} - \"#{pinName}\""
-
rescue
-
raise "\n#{item} doesn't appear to have a physical location in this configuration."
-
end
-
end
-
end
-
generate_field
-
else
-
puts 'Unsupported argument type.'
-
end
-
end
-
-
# .plot_coord can be called explicitly and accepts string arguments in the form
-
# of jedec standard BGA coordinate naming conventions.
-
#
-
# $dut.package = :t4240
-
# $dut.package.plot_coord("A2")
-
#
-
1
def plot_coord(coord, marker = nil)
-
prepare_plot if field.nil?
-
if plottable
-
if coord.is_a?(String)
-
found_pins = []
-
owner.pins.map { |pin| found_pins << pin[1] if coord == pin[1].location.to_s }
-
if marker.nil?
-
marker = initial(coord.to_s)
-
while @groups.index { |grpName| grpName =~ /#{marker} -/ }
-
marker.next!
-
marker = '0' unless marker.size < 2
-
end
-
found_pins.each do |pin|
-
coordinates = coordinate(pin.location)
-
@field[coordinates[0]][coordinates[1]] = [marker.white_on_blue + ' ']
-
@groups.delete_if { |group| /#{coord.to_s}/ =~ group }
-
@groups << "#{marker} - #{found_pins[0].name} - #{found_pins[0].location}"
-
end
-
else
-
coordinates = coordinate(found_pins[0].location)
-
@field[coordinates[0]][coordinates[1]] = [marker.white_on_blue + ' ']
-
@groups.delete_if { |group| /#{coord.to_s}/ =~ group }
-
@groups << "#{marker} - #{found_pins[0].name} - #{found_pins[0].location}"
-
end
-
if found_pins.size > 0
-
generate_field
-
else
-
puts "Coordinate not recognized. Jedec convention: <row><col>. E.g., A1.\nCould be power/ground pin. .plot(\"power\") or .plot(\"ground\")."
-
end
-
else
-
puts 'Unsupported argument type.'
-
end
-
end
-
end
-
1
alias_method :plot_coordinate, :plot_coord
-
-
1
def quote_regex(regex_statement)
-
with_escapes = regex_statement
-
with_escapes.gsub!('[', '\[')
-
with_escapes.gsub!(']', '\]')
-
with_escapes.gsub!('^', '\^')
-
with_escapes
-
end
-
-
1
def plot_ceo
-
scramble = [' +....~~~~~:,,..::~~:,......,:~==~,,....,:~==~~::,:~~,., ',
-
' ...++~:,,:~~~~~======~::::,,,,::=======~~::,, ',
-
'.................????????+,,,,::::::,,,.,,..,,,,:::????......................:+',
-
' ,.,:~~~~~:,~~~~:~:~~=~:~~~==++====::~~~===~~=~~==:,, ',
-
' ,....~++?:::,,::::~~~=======+============~~~:,~ ',
-
' ....+++,:,,::::~~~==========~===========~:,:,~ ',
-
' ,........~~~~~~============================~........: ',
-
' ......,~~~~~~~===~++==+=+++++++++++++++++====~===~~~.....~ ',
-
' ,,,::::::~~~~:,,:::,,,,:,,,,......:~~:~::,,: ',
-
' .....,::~~~~=============+++++++++++++++++=======~~~:..... ',
-
' +.,::::::~~~=~=~~~~~~~====~~~~~:::~~==~~::::, ',
-
' .......:~~~~=~=======++++++++++++++++++======~~........ ',
-
' +:,,::~===+++++====+====~~~=====~====+++++++++=~~,~+ ',
-
'.....................,===+??????? ,..........????????=:,....................',
-
'...................=????????++++:,::::::::::::::???????~.......................',
-
'......................+++++?????? .............=??????+:,....................',
-
' =~:,::~~++=~=::,,,,,,,,,: ',
-
' +.........,,,,,,...,,,:,,,........,,,........,? ',
-
' :....::~~~=========+++++++++++++++++++++++=======~~:.... ',
-
'...................,+???????????? ?::,:,,,,,,::????????=,......................',
-
'..................~???????++++,::,::::::~~:::::::+?????:.......................',
-
' :......:~~~~~~~=====+++++++++++++++++++++=+=====~~~......= ',
-
' ::,::::~~===++===:.,~~~~~~===~~~~~,,:======~~::, ',
-
' ,,,::::~~~~~~~~~=~~===~~~~===~=====~~~~~~:::,~ ',
-
'....................::~+??????? =~+ ?????+?+=,.....................',
-
' :,.,~~~~~,:~~~......~,,..:~=+++=~:,..:,.......:~~=~:,~ ',
-
' :........:~~~~~~~~~===============~~~====:,....... ',
-
' ::,::~~===+++++++++==~~~~=======~~====+++++==~~::: ',
-
' ....,:~~~~~========++++++++++++++++++++++=+=====~~~:.... ',
-
' ,......,~~~~~~~=======+++++++++++++++++++=====~~~....... ',
-
' ,........+???~:::,,,,,:~~~~==============~~~~:,,?...? ',
-
' ...,~~~~~,.,~=====~:,.,.,:~==+==:,,,,.,,.,,~==~::=~,,~ ',
-
' ,........,:::::,:::::~~~~~~~:::,,,,,,~::.......,= ',
-
' ::,::~:~~======~:.,~::,,,,,,,,,,,~=.,:~===~~~::, ',
-
'.........................???++?????====.........?????~,??+::,..................',
-
' ......:~:~~~=~=========+=++++++++++++++++=======~~~~:..... ',
-
' ,...,..,.,,.,,,,,,.:::,.,,........? ',
-
'.......................?++??? ...............??????+::....................',
-
'...........................?+????? ??~....,...+?????????=::..................',
-
' ......:~:~~~~~====~======~==++===+++++===========~~~.....~ ',
-
'..........................+????? ?????+=........??????????~::..................',
-
' ....::~~~====+++++=======++++++++++==============~:...~ ',
-
'..........................?+?????? ?++=,........????????~+:::..................',
-
' .......:~~~~~~======+++++++++++++++++++++=====~~~:...... ',
-
'.......................~?????? :.......,.........??????~:,...................',
-
' ,,:::::~~=~~~:,~=~~~~~~:~~~~~~~====:~~=~~::::: ',
-
' ~,,:::::~=====:..~~~,.,:,,.,,,.~==~,:~===~~::,:= ',
-
' :,....::::,:,,,,,,,.,,,,,,..,:= ',
-
' ..............+?????=,,,,::,,,::~:~~~~~~~~~:::::,,::??..........~ ',
-
' ,,.:~~====~~~==~=~~~=====~~==+======~~~~~~===~===:,: ',
-
' ........~~~~~======+++++++++++++++++++=======~~....... ',
-
' ....::~~~~===============================~~~:~===~:... ',
-
' ,...........,...................,..........: ',
-
' +....:~~~~~~~,,..,,,,,.,:~~~~~===~~~~:,.,,,,,,,:~~~~.., ',
-
' .......~~~~~~~~=~==+==+++++++++++++=++++++==~====~~,.....: ',
-
'.................~???????+++,,,:,::::::,,,,,:,:::,?????........................',
-
' ~,.,:~==================~~~==+==~=========++++==~::: ',
-
' ,....,,,,,......,,,.,..:,:,..........:~ ',
-
'........................?????? ++=.,,:.........++=:?????=::...................',
-
' .......+++?:~:,,,,::~~~==================~~~:,,.? ',
-
'...................,:+?????????????? =:~:::::~ ???????++:......................',
-
' :....,::~~~~==~====+=+++++++++==+++++++++========~~~:....: ',
-
'.....................~~~+?????? ~:....: ????????=:.....................',
-
'....................::+?????????????????::,. ????????++~......................',
-
' ,...,,:,.,................,,::..........,= ',
-
' ~::,:~~~====+++++==~:~~~~==++===~=::~==++++==:::,: ',
-
' ~,.,~~~~~~~,..:~...~=~::,~~=++++=~~::~~~~~~:,,,~==~:, ',
-
'........................~??????=+?+~:~~.........???..????=::...................',
-
' ,..........+????:::,::,,,:~~~~~~==~~~~~~~~~~~~,.:=?......: ',
-
'................=???????~,,,::,:,:,,,.......,,,,,,:~???..................= ',
-
' ,,::::::~~~~~~~~~~=====~~~~=====~~~~=~~~::::, ',
-
' ........~~~~~========+++++++==++++=========~~........ ',
-
' :..+,,,:~~~~~======~~~==~===~~~~======~~~:::, ',
-
' ~.,,::~:~~~~~~~~~~~~~~~=======~=====~~~~::,: ',
-
' ..............:??????+,,,,:,,,,:,,,,,,,,....,..,,::~??.............., ',
-
' .,:~~:~:::::.....,:~=====~:::~~~:,::~~::,, ']
-
pw = [16, 47, 75, 168, 53, 36, 57, 116, 94, 21, 64, 52, 13, 95, 1, 17, 32, 38, 4, 142,
-
26, 6, 89, 134, 44, 71, 50, 40, 170, 149, 11, 29, 167, 27, 120, 43, 21, 107, 72, 14, 54,
-
7, 3, 58, 55, 39, 35, 47, 113, 5, 9, 61, 162, 123, 39, 28, 18, 36, 35, 91, 41, 51, 160,
-
128, 54, 53, 0, 138, 165, 125, 31, 25, 19, 155, 25, 66, 3, 15, 49, 96, 49, 56, 158, 100,
-
147, 12, 27, 101, 56, 12, 65, 22, 124, 106, 11, 33, 46, 26, 103, 7, 45, 23, 46, 97, 9,
-
70, 10, 109, 119, 22, 8, 75, 151, 65, 52, 73, 72, 99, 30, 17, 1, 5, 90, 76, 81, 4, 59,
-
50, 82, 84, 30, 68, 102, 148, 80, 48, 74, 129, 137, 132, 69, 2, 140, 34, 144, 55, 20,
-
150, 24, 143, 14, 19, 86, 8, 67, 60, 63, 2, 62, 146, 24, 62, 0, 104, 68, 13, 15, 173, 79,
-
63, 37, 44, 93, 85, 60, 58, 67]
-
y = []
-
(0..71).each { |n| y << n + n / 2 * 3 }
-
y.each do |z|
-
puts scramble[pw[z]]
-
end
-
'Oh, hello!'
-
end
-
end
-
end
-
2
require 'thor/group'
-
-
2
require 'active_support'
-
2
require 'active_support/core_ext/object/blank'
-
2
require 'active_support/core_ext/kernel/singleton_class'
-
2
require 'active_support/core_ext/array/extract_options'
-
2
require 'active_support/core_ext/hash/deep_merge'
-
2
require 'active_support/core_ext/module/attribute_accessors'
-
2
require 'active_support/core_ext/string/inflections'
-
-
2
module Origen
-
2
module CodeGenerators
-
2
autoload :Base, 'origen/code_generators/base'
-
2
autoload :Actions, 'origen/code_generators/actions'
-
-
# Remove the color from output.
-
2
def self.no_color!
-
Thor::Base.shell = Thor::Shell::Basic
-
end
-
-
2
def self.origen_generators
-
21
@origen_generators ||= {}
-
end
-
-
2
def self.plugin_generators
-
36
@plugin_generators ||= {}
-
end
-
-
2
def self.load_generators
-
15
return if @generators_loaded
-
# Load Origen's generators
-
1
require_relative 'code_generators/block_common'
-
1
require_relative 'code_generators/dut'
-
1
require_relative 'code_generators/block'
-
1
require_relative 'code_generators/feature'
-
1
require_relative 'code_generators/model'
-
1
require_relative 'code_generators/klass'
-
1
require_relative 'code_generators/module'
-
# Load generators from plugins, TBD what the API will be here
-
1
@generators_loaded = true
-
end
-
-
# Loaded separately so as not to pollute the generated list of generators available to users
-
2
def self.load_internal_generators
-
return if @internal_generators_loaded
-
require_relative 'code_generators/semver'
-
require_relative 'code_generators/timever'
-
@internal_generators_loaded = true
-
end
-
-
# Receives a namespace, arguments and the behavior to invoke the generator.
-
# It's used as the default entry point for generate, destroy and update
-
# commands.
-
2
def self.invoke(name, args = ARGV, config = {})
-
15
load_generators
-
15
if klass = find_by_name(name)
-
15
args << '--help' if args.empty? && klass.arguments.any?(&:required?)
-
15
klass.start(args, config)
-
end
-
end
-
-
# Like invoke, but will also make internal-use only generators available
-
# commands.
-
2
def self.invoke_internal(name, args = ARGV, config = {})
-
load_internal_generators
-
invoke(name, args, config)
-
end
-
-
2
def self.find_by_name(name)
-
15
names = name.split(':')
-
15
case names.size
-
when 1
-
15
gen = origen_generators[names.first]
-
15
return gen if gen
-
when 2
-
if names.first == 'origen'
-
gen = origen_generators[names.first]
-
else
-
gen = plugin_generators[names.first][names.last]
-
end
-
return gen if gen
-
end
-
puts "Couldn't find a code generator named: #{name}"
-
puts
-
puts 'This is the list of available generators:'
-
puts
-
print_generators
-
puts
-
end
-
-
# Show help message with available generators.
-
2
def self.help(command = 'new')
-
puts <<-END
-
Add pre-built features and code snippets to your application.
-
-
This command will generate code for your application to implement a given feature. In some
-
cases this will be a complete feature and in others it will provide a starting point for you
-
to further customize.
-
-
END
-
puts "Usage: origen #{command} FEATURE [args] [options]"
-
puts
-
puts 'General options:'
-
puts " -h, [--help] # Print feature's options and usage"
-
puts ' -p, [--pretend] # Run but do not make any changes'
-
puts ' -f, [--force] # Overwrite files that already exist'
-
puts ' -s, [--skip] # Skip files that already exist'
-
puts ' -q, [--quiet] # Suppress status output'
-
puts
-
puts "The available features are listed below, run 'origen new <feature> -h' for more info."
-
puts
-
-
print_generators
-
puts
-
end
-
-
2
def self.print_generators
-
load_generators
-
origen_generators.each do |name, _gen|
-
puts name
-
end
-
plugin_generators.each do |namespace, generators|
-
next if namespace.to_s == 'origen_app_generators'
-
puts
-
generators.each do |_name, gen|
-
puts "#{namespace}:#{gen}"
-
end
-
end
-
end
-
end
-
end
-
2
require 'open-uri'
-
2
require 'set'
-
-
2
module Origen
-
2
module CodeGenerators
-
# Common helpers available to all Origen code generators.
-
# Some of these have been copied from Rails and don't make a lot of sense in an Origen context,
-
# however they are being kept around for now as they serve as good examples of how to write
-
# generator helpers.
-
2
module Actions
-
2
def initialize(*args) # :nodoc:
-
15
if args.last.is_a?(Hash)
-
15
@config = args.last.delete(:config) || {}
-
end
-
15
@required_acronyms = Set.new
-
15
super
-
15
@in_group = nil
-
end
-
-
2
def config
-
@config
-
end
-
-
2
def underscored_app_namespace
-
96
Origen.app.namespace.to_s.underscore
-
end
-
-
# Equivalent to calling name.camelcase, but this will identify the need to register any acronyms
-
# necessary to ensure the camelcased name can be translated back to the original name by the
-
# underscore method.
-
# The required acronyms will be saved to an instance variable, @required_acronyms, and calling
-
# the add_acronyms will add the code to register them to the current application.
-
2
def camelcase(name)
-
209
name = name.to_s
-
209
name.split('_').each do |n|
-
# Numbers won't be recognized as a split point when going back to underscore, so need to
-
# register this field beginning with a number as an acronym
-
280
@required_acronyms << n if n =~ /^\d/
-
end
-
209
name.camelcase
-
end
-
-
2
def add_acronyms
-
14
unless @required_acronyms.empty?
-
top_level_file = File.join('app', 'lib', "#{underscored_app_namespace}.rb")
-
if File.exist?(top_level_file)
-
require_origen = "require 'origen'\n"
-
prepend_to_file top_level_file, require_origen
-
comment = "# The following acronyms are required to ensure that auto-loading works\n# properly with some of this application's class names\n"
-
insert_into_file top_level_file, comment, after: require_origen
-
@required_acronyms.each do |acronym|
-
insert_into_file top_level_file, "Origen.register_acronym '#{acronym}'\n", after: comment
-
end
-
end
-
end
-
end
-
-
# Adds an autoload statement for the given resource name into +app/lib/my_app_name.rb+
-
#
-
# An array of namespaces can optionally be supplied in the arguments. The name and namespaces
-
# should all be lower cased and underscored.
-
#
-
# add_autoload "my_model", namespaces: ["my_namespace", "my_other_namespace"]
-
2
def add_autoload(name, options = {})
-
namespaces = Array(options[:namespaces])
-
# Remove the app namespace if present, we will add the autoload inside the top-level module block
-
namespaces.shift if namespaces.first == app_namespace
-
top_level_file = File.join('app', 'lib', "#{underscored_app_namespace}.rb")
-
if namespaces.empty?
-
line = " autoload :#{camelcase(name)}, '#{underscored_app_namespace}/#{name}'\n"
-
insert_into_file top_level_file, line, after: /module #{Origen.app.namespace}\n/
-
else
-
contents = File.read(top_level_file)
-
regex = "module #{Origen.app.namespace}\s*(#.*)?\n"
-
indent = ''
-
namespaces.each do |namespace|
-
indent += ' '
-
new_regex = regex + "(\n|.)*^\s*module #{camelcase(namespace)}\s*(#.*)?\n"
-
unless contents =~ Regexp.new(new_regex)
-
lines = "#{indent}module #{camelcase(namespace)}\n"
-
lines << "#{indent}end\n"
-
insert_into_file top_level_file, lines, after: Regexp.new(regex), force: true
-
end
-
regex = new_regex
-
end
-
line = "#{indent} autoload :#{camelcase(name)}, '#{underscored_app_namespace}/#{namespaces.join('/')}/#{name}'\n"
-
insert_into_file top_level_file, line, after: Regexp.new(regex)
-
end
-
end
-
-
# Removes (comments out) the specified configuration setting from +config/application.rb+
-
#
-
# comment_config :semantically_version
-
2
def comment_config(name, options = {})
-
# Set the message to be shown in logs
-
log :comment, name
-
-
file = File.join(Origen.root, 'config', 'application.rb')
-
comment_lines(file, /^\s*config.#{name}\s*=.*\n/)
-
end
-
-
# Adds an entry into +config/application.rb+
-
2
def add_config(name, value, options = {})
-
# Set the message to be shown in logs
-
message = name.to_s
-
if value ||= options.delete(:value)
-
message << " (#{value})"
-
end
-
log :insert, message
-
-
file = File.join(Origen.root, 'config', 'application.rb')
-
value = quote(value) if value.is_a?(String)
-
value = ":#{value}" if value.is_a?(Symbol)
-
insert_into_file file, " config.#{name} = #{value}\n\n", after: /^\s*class.*\n/
-
end
-
-
# Adds an entry into +Gemfile+ for the supplied gem.
-
#
-
# gem "rspec", group: :test
-
# gem "technoweenie-restful-authentication", lib: "restful-authentication", source: "https://gems.github.com/"
-
# gem "rails", "3.0", git: "git://github.com/rails/rails"
-
2
def gem(name, version, options = {})
-
# Set the message to be shown in logs. Uses the git repo if one is given,
-
# otherwise use name (version).
-
parts, message = [quote(name)], name
-
if version ||= options.delete(:version)
-
parts << quote(version)
-
message << " (#{version})"
-
end
-
message = options[:git] if options[:git]
-
-
log :gemfile, message
-
-
options.each do |option, value|
-
parts << "#{option}: #{quote(value)}"
-
end
-
-
in_root do
-
str = "gem #{parts.join(', ')}"
-
str = ' ' + str if @in_group
-
str = "\n" + str
-
append_file 'Gemfile', str, verbose: false
-
end
-
end
-
-
# Wraps gem entries inside a group.
-
#
-
# gem_group :development, :test do
-
# gem "rspec-rails"
-
# end
-
2
def gem_group(*names, &block)
-
name = names.map(&:inspect).join(', ')
-
log :gemfile, "group #{name}"
-
-
in_root do
-
append_file 'Gemfile', "\ngroup #{name} do", force: true
-
-
@in_group = true
-
instance_eval(&block)
-
@in_group = false
-
-
append_file 'Gemfile', "\nend\n", force: true
-
end
-
end
-
-
# Add the given source to +Gemfile+
-
#
-
# add_source "https://gems.github.com/"
-
2
def add_source(source, _options = {})
-
log :source, source
-
-
in_root do
-
prepend_file 'Gemfile', "source #{quote(source)}\n", verbose: false
-
end
-
end
-
-
# Adds a line inside the Application class for <tt>config/application.rb</tt>.
-
#
-
# If options <tt>:env</tt> is specified, the line is appended to the corresponding
-
# file in <tt>config/environments</tt>.
-
#
-
# environment do
-
# "config.autoload_paths += %W(#{config.root}/extras)"
-
# end
-
#
-
# environment(nil, env: "development") do
-
# "config.autoload_paths += %W(#{config.root}/extras)"
-
# end
-
2
def environment(data = nil, options = {})
-
sentinel = /class [a-z_:]+ < Rails::Application/i
-
env_file_sentinel = /Rails\.application\.configure do/
-
data = yield if !data && block_given?
-
-
in_root do
-
if options[:env].nil?.map(&:camelcase).join('::')
-
inject_into_file 'config/application.rb', "\n #{data}", after: sentinel, verbose: false
-
else
-
Array(options[:env]).each do |env|
-
inject_into_file "config/environments/#{env}.rb", "\n #{data}", after: env_file_sentinel, verbose: false
-
end
-
end
-
end
-
end
-
2
alias_method :application, :environment
-
-
# Run a command in git.
-
#
-
# git :init
-
# git add: "this.file that.rb"
-
# git add: "onefile.rb", rm: "badfile.cxx"
-
2
def git(commands = {})
-
if commands.is_a?(Symbol)
-
run "git #{commands}"
-
else
-
commands.each do |cmd, options|
-
run "git #{cmd} #{options}"
-
end
-
end
-
end
-
-
# Create a new file in the lib/ directory. Code can be specified
-
# in a block or a data string can be given.
-
#
-
# lib("crypto.rb") do
-
# "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'"
-
# end
-
#
-
# lib("foreign.rb", "# Foreign code is fun")
-
2
def lib(filename, data = nil, &block)
-
log :lib, filename
-
create_file("lib/#{filename}", data, verbose: false, &block)
-
end
-
-
# Create a new +Rakefile+ with the provided code (either in a block or a string).
-
#
-
# rakefile("bootstrap.rake") do
-
# project = ask("What is the UNIX name of your project?")
-
#
-
# <<-TASK
-
# namespace :#{project} do
-
# task :bootstrap do
-
# puts "I like boots!"
-
# end
-
# end
-
# TASK
-
# end
-
#
-
# rakefile('seed.rake', 'puts "Planting seeds"')
-
2
def rakefile(filename, data = nil, &block)
-
log :rakefile, filename
-
create_file("lib/tasks/#{filename}", data, verbose: false, &block)
-
end
-
-
# Generate something using a generator from Rails or a plugin.
-
# The second parameter is the argument string that is passed to
-
# the generator or an Array that is joined.
-
#
-
# generate(:authenticated, "user session")
-
2
def generate(what, *args)
-
log :generate, what
-
argument = args.flat_map(&:to_s).join(' ')
-
-
in_root { run_ruby_script("bin/rails generate #{what} #{argument}", verbose: false) }
-
end
-
-
# Reads the given file at the source root and prints it in the console.
-
#
-
# readme "README"
-
2
def readme(path)
-
log File.read(find_in_source_paths(path))
-
end
-
-
# Should probably move to its own file, these are general helpers rather than actions
-
2
module Helpers
-
# Returns the depth of the given file, where depth is the number of modules and classes it contains
-
2
def internal_depth(file)
-
depth = 0
-
File.readlines(file).each do |line|
-
if line =~ /^\s*(end|def)/
-
return depth
-
elsif line =~ /^\s*(module|class)/
-
depth += 1
-
end
-
end
-
end
-
-
# Only executes the given block if the given file does not already define the given method, where the
-
# block would normally go on to insert the method.
-
#
-
# See the ensure_define_sub_blocks method in the sub_blocks.rb generator for a usage example.
-
2
def unless_has_method(filepath, name)
-
unless File.read(filepath) =~ /^\s*def #{name}(\(|\s|\n)/
-
yield
-
end
-
end
-
-
# Executes the given block unless the given string is lower cased and underscored and doesn't start
-
# with a number of contain any special characters
-
2
def unless_valid_underscored_identifier(str)
-
18
if str =~ /[^0-9a-z_]/ || str =~ /^[0-9]/
-
yield
-
end
-
end
-
-
2
def validate_resource_path(name)
-
15
name.split('/').each do |n|
-
18
unless_valid_underscored_identifier(n) do
-
Origen.log.error "All parts of a resource name must be lower-cased, underscored and start with letter, '#{n}' is invalid"
-
exit 1
-
end
-
end
-
15
name
-
end
-
2
alias_method :validate_resource_name, :validate_resource_path
-
-
# Converts a path to a resource identifier, by performing the following operations on the given path:
-
# 1) Convert any absolute paths to relative
-
# 2) Removes any leading blocks/, lib/ or application namespaces
-
# 3) Remove any derivatives directories from the path
-
# 3) Removes any trailing .rb
-
#
-
# Examples:
-
#
-
# /my/code/my_app/app/blocks/dut/derivatives/falcon => dut/falcon
-
# app/lib/my_app/eagle.rb => eagle
-
2
def resource_path(path)
-
69
path = Pathname.new(path).expand_path.relative_path_from(Pathname.pwd).to_s
-
69
path = path.sub('.rb', '')
-
69
path = path.split('/')
-
69
from_block_dir_path = false
-
69
path.shift if path.first == 'app'
-
69
path.shift if path.first == 'lib'
-
69
if path.first == 'blocks'
-
23
path.shift
-
23
from_block_dir_path = true
-
end
-
69
path.shift if path.first == underscored_app_namespace
-
69
if path.include?('derivatives')
-
15
path.delete('derivatives')
-
15
from_block_dir_path = true
-
end
-
69
if from_block_dir_path
-
23
path.delete('sub_blocks')
-
23
path.pop if path.last == 'model'
-
23
if path.last == 'controller'
-
2
path.pop
-
2
path << "#{path.pop}_controller"
-
end
-
end
-
69
path.join('/')
-
end
-
-
# Returns a Pathname to the blocks directory that should contain the given class name. No checking is
-
# done of the name and it is assumed that it is a valid class name including the application namespace.
-
2
def class_name_to_blocks_dir(name)
-
1
name = name.split('::')
-
1
name.shift # Drop the application name
-
1
dir = Origen.root.join('app', 'blocks')
-
1
name.each_with_index do |n, i|
-
1
if i == 0
-
1
dir = dir.join(n.underscore)
-
else
-
dir = dir.join('derivatives', n.underscore)
-
end
-
end
-
1
dir
-
end
-
-
# Returns a Pathname to the lib directory file that should contain the given class name. No checking is
-
# done of the name and it is assumed that it is a valid class name including the application namespace.
-
2
def class_name_to_lib_file(name)
-
3
name = name.split('::')
-
3
dir = Origen.root.join('app', 'lib')
-
3
name.each_with_index do |n, i|
-
6
dir = dir.join(i == name.size - 1 ? "#{n.underscore}.rb" : n.underscore)
-
end
-
3
dir
-
end
-
-
2
def resource_path_to_blocks_dir(path)
-
34
name = resource_path(path).split('/') # Ensure this is clean, don't care about performance here
-
34
dir = Origen.root.join('app', 'blocks')
-
34
name.each_with_index do |n, i|
-
73
if i == 0
-
34
dir = dir.join(n.underscore)
-
else
-
39
if dir.join('sub_blocks', n.underscore).exist?
-
13
dir = dir.join('sub_blocks', n.underscore)
-
else
-
26
dir = dir.join('derivatives', n.underscore)
-
end
-
end
-
end
-
34
dir
-
end
-
-
2
def resource_path_to_lib_file(path)
-
4
name = resource_path(path).split('/') # Ensure this is clean, don't care about performance here
-
4
dir = Origen.root.join('app', 'lib', underscored_app_namespace)
-
4
name.each_with_index do |n, i|
-
4
dir = dir.join(i == name.size - 1 ? "#{n.underscore}.rb" : n.underscore)
-
end
-
4
dir
-
end
-
-
2
def resource_path_to_class(path)
-
18
name = resource_path(path).split('/') # Ensure this is clean, don't care about performance here
-
18
name.unshift(underscored_app_namespace)
-
61
name.map { |n| camelcase(n) }.join('::')
-
end
-
-
# Adds :class and :module identifiers to an array of namespaces
-
#
-
# ["my_app", "models", "bist"] => [[:module, "my_app"], [:module, "models"], [:class, "bist"]]
-
#
-
2
def add_type_to_namespaces(namespaces)
-
10
identifier = nil
-
10
namespaces.map do |namespace|
-
22
if identifier
-
12
identifier += "::#{camelcase(namespace)}"
-
else
-
10
identifier = camelcase(namespace)
-
end
-
begin
-
22
const = identifier.constantize
-
22
[const.is_a?(Class) ? :class : :module, namespace]
-
rescue NameError
-
[:module, namespace]
-
end
-
end
-
end
-
end
-
2
include Helpers
-
-
2
protected
-
-
# Define log for backwards compatibility. If just one argument is sent,
-
# invoke say, otherwise invoke say_status. Differently from say and
-
# similarly to say_status, this method respects the quiet? option given.
-
2
def log(*args)
-
if args.size == 1
-
say args.first.to_s unless options.quiet?
-
else
-
args << (behavior == :invoke ? :green : :red)
-
say_status(*args)
-
end
-
end
-
-
2
def in_root
-
Dir.chdir(Origen.root) do
-
yield
-
end
-
end
-
-
# Surround string with single quotes if there are no quotes,
-
# otherwise fall back to double quotes
-
2
def quote(value)
-
return value.inspect unless value.is_a? String
-
-
if value.include?("'")
-
value.inspect
-
else
-
"'#{value}'"
-
end
-
end
-
end
-
end
-
end
-
2
require 'thor/group'
-
2
module Origen
-
2
module CodeGenerators
-
2
class Error < Thor::Error # :nodoc:
-
end
-
-
2
class Base < Thor::Group
-
2
include Thor::Actions
-
2
include Origen::CodeGenerators::Actions
-
-
2
add_runtime_options!
-
2
strict_args_position!
-
-
# Convenience method to get the top-level namespace from the class name.
-
# It is returned as a lower cased and underscored string.
-
2
def self.namespace(name = nil)
-
60
@namespace ||= begin
-
24
names = super.split(':')
-
24
if names.size == 1
-
nil
-
else
-
24
names.first.sub(/^r_gen/, 'origen')
-
end
-
end
-
end
-
-
# Sets the base_name taking into account the current class namespace.
-
2
def self.name
-
72
@name ||= begin
-
24
name = to_s.split('::').last.sub(/(CodeGenerator|Generator)$/, '').underscore
-
24
if name == 'klass'
-
1
'class'
-
23
elsif name == 'mod'
-
1
'module'
-
else
-
22
name
-
end
-
end
-
end
-
-
# Cache source root and add lib/generators/base/generator/templates to
-
# source paths.
-
2
def self.inherited(base) #:nodoc:
-
24
super
-
24
if base.name && base.name !~ /Base$/
-
24
if base.namespace == 'origen'
-
6
Origen::CodeGenerators.origen_generators[base.name] = base
-
else
-
18
Origen::CodeGenerators.plugin_generators[base.namespace] ||= {}
-
18
Origen::CodeGenerators.plugin_generators[base.namespace][base.name] = base
-
end
-
end
-
# Give all generators access to Origen core files in their source path,
-
# with their own app as highest priority
-
24
base.source_paths << Origen.root if Origen.app_loaded?
-
24
base.source_paths << Origen.top
-
end
-
-
2
def self.banner
-
"origen new #{namespace == 'origen' ? '' : namespace + ':'}#{name} [options]"
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
module CodeGenerators
-
1
class Block < Origen::CodeGenerators::Base
-
1
include BlockCommon
-
-
# class_option :duts, type: :boolean, desc: 'Instantiate the new sub-block in all DUT models', default: true
-
# class_option :instance, desc: 'The main NAME argument will be the name given to the model and the instantiated sub-block, optionally provide a different name for the instance'
-
-
1
def self.banner
-
'origen new block [TYPE/]DERIVATIVE [BLOCK]'
-
end
-
-
1
desc <<-END
-
This generator creates a block (e.g. to represent RAM, ATD, Flash, DAC, etc.) and all of the associated
-
resources for it, e.g. a model, controller, timesets, parameters, etc.
-
-
The TYPE and DERIVATIVE names should be given in lower case (e.g. flash/flash2kb, atd/atd16), optionally with
-
additional parent sub-block names after the initial type.
-
-
Alternatively, a reference to an existing BLOCK can be added, in which case a nested block will be created
-
within that block's sub_blocks directory, rather than a primary block.
-
Note that nested blocks do not support derivatives or inheritance and should therefore only be used for
-
relatively simple entities which are tightly coupled to a parent block.
-
-
Any parent block(s) will be created if they don't exist, but they will not be modified if they do.
-
-
Examples:
-
origen new block atd/atd8bit # Creates app/blocks/atd/derivatives/atd8bit/...
-
origen new block atd/atd16bit # Creates app/blocks/atd/derivatives/atd16bit/...
-
origen new block nvm/flash/flash2kb # Creates app/blocks/nvm/derivatives/flash/derivatives/flash2kb/...
-
-
# Example of creating a nested sub-block
-
origen new block nvm/flash/flash2kb bist # Creates app/blocks/nvm/derivatives/flash/derivatives/flash2kb/sub_blocks/bist/...
-
END
-
-
1
def validate_args
-
5
if args.size > 2 || args.size == 0
-
msg = args.size == 0 ? 'At least one argument is' : 'No more than two arguments are'
-
msg << " expected by the block generator, e.g. 'origen new block atd/atd16bit', 'origen new block sampler app/blocks/atd/derivatives/atd16bit"
-
puts msg
-
exit 1
-
end
-
-
5
if args.size == 2
-
3
validate_args_common(args.last)
-
else
-
2
validate_args_common
-
end
-
-
5
@nested = args.size == 2
-
5
if !@nested && args.first.split('/').size == 1
-
msg = "You must supply a leading type to the name of the block, e.g. 'origen new block atd/atd16bit'"
-
puts msg
-
exit 1
-
end
-
5
if @nested && args.last.split('/').size != 1
-
msg = "No leading type is allowed when generating a nested block, e.g. 'origen new block sampler app/blocks/atd/derivatives/atd16bit"
-
puts msg
-
exit 1
-
end
-
end
-
-
1
def setup
-
5
@generate_model = true
-
5
@generate_pins = false
-
5
@generate_timesets = !@nested
-
5
@generate_parameters = !@nested
-
5
if @nested
-
3
@final_name = args.last
-
3
@fullname = resource_path_to_class(args.first)
-
3
@dir = resource_path_to_blocks_dir(args.first).join('sub_blocks', @final_name)
-
3
@namespaces = add_type_to_namespaces(@fullname.split('::').map(&:underscore))
-
else
-
2
extract_model_name
-
end
-
5
create_files
-
end
-
-
1
def instantiate_sub_block
-
5
if @nested
-
# First create the parent's sub_blocks.rb file if it doesn't exist
-
3
f = "#{@dir.parent}.rb"
-
3
unless File.exist?(f)
-
@nested = false
-
orig_fullname = @fullname
-
orig_resouce_path = @resource_path
-
@fullname = @fullname.split('::')
-
@fullname.pop
-
@fullname = @fullname.join('::')
-
@resource_path = @resource_path.split('/')
-
@resource_path.pop
-
@resource_path = @resource_path.join('/')
-
template 'templates/code_generators/sub_blocks.rb', f
-
@fullname = orig_fullname
-
@resource_path = orig_resouce_path
-
@nested = true
-
end
-
-
3
line = "sub_block :#{@final_name}, class_name: '#{@fullname}'#, base_address: 0x4000_0000"
-
3
append_to_file f, "\n#{line}"
-
else
-
2
@line = "sub_block :#{@final_namespaces[1]}, class_name: '#{class_name}'#, base_address: 0x4000_0000"
-
-
2
unless duts.empty?
-
2
puts
-
2
@dut_index = [nil]
-
2
index = 1
-
2
duts.each do |name, children|
-
2
index = print_dut(name, index, children, 0)
-
end
-
2
puts
-
2
puts 'DO YOU WANT TO INSTANTIATE THIS SUB-BLOCK IN YOUR DUT MODELS?'
-
2
puts
-
2
puts 'If so enter the number(s) of the DUT(s) you wish to add it to from the list above, separating multiple entries with a space'
-
2
puts '(note that adding it to a parent DUT in the hierarchy will already be adding it to all of its children).'
-
2
puts
-
2
response = ask 'Enter the DUT number(s), or just press return to skip:'
-
-
2
done = []
-
2
response.strip.split(/\s+/).each do |index|
-
1
index = index.to_i
-
1
target = @dut_index[index]
-
1
if target
-
# Don't add the sub-block to children if we've already added it to the parent, this will
-
# cause an already defined sub-block error since it will be added by both instantiations
-
1
unless done.any? { |c| target =~ /^#{c}::/ }
-
1
done << target
-
1
sub_blocks = class_name_to_blocks_dir(target).join('sub_blocks.rb')
-
1
unless sub_blocks.exist?
-
orig = @fullname
-
@fullname = target
-
template 'templates/code_generators/sub_blocks.rb', sub_blocks
-
@fullname = orig
-
end
-
1
@sub_block_instantiated = true
-
1
append_to_file sub_blocks, "\n#{@line}"
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
def completed
-
5
add_acronyms
-
5
puts
-
5
if @nested
-
3
puts 'New sub-block created and instantiated.'.green
-
else
-
2
if @sub_block_instantiated
-
1
puts 'New sub-block created and instantiated within your DUT(s) as:'.green + " dut.#{@final_namespaces[1]}"
-
else
-
1
puts 'New sub-block created, you can instantiate it within your blocks like this:'.green
-
1
puts
-
1
puts " #{@line}"
-
end
-
end
-
5
puts
-
end
-
-
1
private
-
-
1
def print_dut(name, index, children, offset)
-
8
@dut_index << name
-
8
puts "#{index}".ljust(2) + ': ' + (' ' * offset) + name
-
8
index += 1
-
8
children.each do |name, children|
-
6
index = print_dut(name, index, children, offset + 1)
-
end
-
8
index
-
end
-
-
# Returns a look up table for all dut blocks defined in this application (only those defined
-
# as blocks, as they all should be now).
-
# This is arranged by hierarchy.
-
1
def duts
-
4
@duts ||= begin
-
2
duts = {}
-
2
dut_dir = Pathname.new(File.join(Origen.root, 'app', 'blocks', 'dut'))
-
2
if dut_dir.exist?
-
2
name = "#{Origen.app.namespace}::DUT"
-
2
duts[name] = {}
-
2
add_derivatives(duts[name], name, dut_dir)
-
end
-
2
duts
-
end
-
end
-
-
1
def add_derivatives(duts, name, dir)
-
8
derivatives = dir.join('derivatives')
-
8
if derivatives.exist?
-
4
derivatives.children.each do |item|
-
6
if item.directory?
-
6
child_name = "#{name}::#{camelcase(item.basename)}"
-
6
duts[child_name] = {}
-
6
add_derivatives(duts[child_name], child_name, item)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
module CodeGenerators
-
# Base generator for the DUT, block and feature generators
-
1
module BlockCommon
-
1
def validate_args_common(arg = nil)
-
8
validate_resource_name(arg || args.first)
-
end
-
-
1
def extract_model_name
-
5
@final_namespaces = args.first.downcase.split('/')
-
-
5
@final_name = @final_namespaces.pop
-
5
@final_name.gsub!(/\.rb/, '')
-
-
5
@final_namespaces.unshift('dut') if @top_level
-
5
@final_namespaces.unshift(underscored_app_namespace)
-
-
5
@model_path = @final_namespaces.dup
-
5
@namespaces = [[:module, @model_path.shift]]
-
end
-
-
1
def create_files
-
# @summary = ask 'Describe your plugin in a few words:'
-
8
@block = true
-
8
@root_class = true
-
-
# Nested sub-blocks do not support inheritance
-
8
unless @nested
-
5
dir = File.join(Origen.root, 'app', 'blocks')
-
5
@fullname = Origen.app.namespace.to_s
-
-
5
@model_path.each do |path|
-
5
dir = File.join(dir, path)
-
5
@name = path
-
5
@fullname += "::#{camelcase(@name)}"
-
5
@resource_path = resource_path(dir)
-
-
5
if @generate_model
-
5
f = File.join(dir, 'model.rb')
-
5
template 'templates/code_generators/model.rb', f unless File.exist?(f)
-
5
f = File.join(dir, 'controller.rb')
-
5
template 'templates/code_generators/controller.rb', f unless File.exist?(f)
-
end
-
5
if @generate_pins
-
3
f = File.join(dir, 'pins.rb')
-
3
template 'templates/code_generators/pins.rb', f unless File.exist?(f)
-
end
-
5
if @generate_timesets
-
5
f = File.join(dir, 'timesets.rb')
-
5
template 'templates/code_generators/timesets.rb', f unless File.exist?(f)
-
end
-
5
if @generate_parameters
-
5
f = File.join(dir, 'parameters.rb')
-
5
template 'templates/code_generators/parameters.rb', f unless File.exist?(f)
-
end
-
5
f = File.join(dir, 'registers.rb')
-
5
template 'templates/code_generators/registers.rb', f unless File.exist?(f)
-
5
f = File.join(dir, 'sub_blocks.rb')
-
5
template 'templates/code_generators/sub_blocks.rb', f unless File.exist?(f)
-
5
f = File.join(dir, 'attributes.rb')
-
5
template 'templates/code_generators/attributes.rb', f unless File.exist?(f)
-
5
dir = File.join(dir, 'derivatives')
-
5
@namespaces << [:class, path]
-
5
@root_class = false
-
-
16
@parent_class = @namespaces.map { |type, name| camelcase(name) }.join('::')
-
end
-
-
6
@parent_class ||= @namespaces.map { |type, name| camelcase(name) }.join('::')
-
end
-
-
8
@name = @final_name
-
8
@fullname += "::#{camelcase(@name)}"
-
8
dir = @dir || File.join(dir, @name)
-
8
@resource_path = resource_path(dir)
-
-
8
if @generate_model
-
7
template 'templates/code_generators/model.rb', File.join(dir, 'model.rb')
-
7
template 'templates/code_generators/controller.rb', File.join(dir, 'controller.rb')
-
end
-
8
if @generate_pins
-
3
template 'templates/code_generators/pins.rb', File.join(dir, 'pins.rb')
-
end
-
8
if @generate_timesets
-
5
template 'templates/code_generators/timesets.rb', File.join(dir, 'timesets.rb')
-
end
-
8
if @generate_parameters
-
5
template 'templates/code_generators/parameters.rb', File.join(dir, 'parameters.rb')
-
end
-
8
template 'templates/code_generators/registers.rb', File.join(dir, 'registers.rb')
-
8
template 'templates/code_generators/sub_blocks.rb', File.join(dir, 'sub_blocks.rb')
-
8
template 'templates/code_generators/attributes.rb', File.join(dir, 'attributes.rb')
-
end
-
-
1
def class_name
-
8
(@final_namespaces + Array(@name)).map { |n| camelcase(n) }.join('::')
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
module CodeGenerators
-
1
class Dut < Origen::CodeGenerators::Base
-
1
include BlockCommon
-
-
1
def self.banner
-
'origen new dut NAME'
-
end
-
-
1
desc <<-END
-
This generator creates a top-level (DUT) block and all of the associated resources for it, e.g. a model,
-
controller, target, timesets, pins, etc.
-
-
The NAME of the DUT should be given in lower case, optionally prefixed by parent DUT name(s) separated
-
by a forward slash.
-
-
Any parent DUT(s) will be created if they don't exist, but they will not be modified if they do.
-
-
Examples:
-
origen new dut falcon # Creates app/blocks/dut/derivatives/falcon/...
-
origen new dut dsp/falcon # Creates app/blocks/dut/derivatives/dsp/derivatives/falcon/...
-
END
-
-
1
def validate_args
-
2
if args.size > 1 || args.size == 0
-
msg = args.size > 1 ? 'Only one' : 'One'
-
msg << " argument is expected by the DUT generator, e.g. 'origen new dut my_soc', 'origen new dut my_family/my_soc"
-
puts msg
-
exit 1
-
end
-
2
validate_args_common
-
end
-
-
1
def setup
-
2
@generate_model = true
-
2
@generate_pins = true
-
2
@generate_timesets = true
-
2
@generate_parameters = true
-
2
@top_level = true
-
2
extract_model_name
-
2
create_files
-
end
-
-
1
def create_target
-
2
contents = ''
-
7
contents << @final_namespaces.map { |n| camelcase(n) }.join('::')
-
2
contents << "::#{camelcase(@name)}.new\n"
-
-
2
create_file "#{Origen.root}/target/#{@name}.rb", contents
-
end
-
-
1
def completed
-
2
add_acronyms
-
2
puts
-
2
puts 'New DUT created, run the following command to target it in your workspace:'.green
-
2
puts
-
2
puts " origen t #{@name}"
-
2
puts
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
module CodeGenerators
-
1
class Feature < Origen::CodeGenerators::Base
-
1
include BlockCommon
-
-
1
def self.banner
-
'origen new feature NAME'
-
end
-
-
1
desc <<-END
-
This generator creates a new feature block, which is similar to a regular block but with no model and controller.
-
Such features can then be loaded (re-used) by multiple blocks within your application code.
-
-
The name of the feature should be given in lower case, optionally prefixed by parent feature name(s) separated
-
by a forward slash.
-
-
Any parent features will be created if they don't exist, but they will not be modified if they do.
-
-
Examples:
-
origen new feature my_feature # Creates app/blocks/my_feature/...
-
origen new feature features/my_feature # Creates app/blocks/features/my_feature/...
-
-
The above can then be loaded to models in your application code via:
-
-
my_model.load_block('my_feature')
-
my_model.load_block('features/my_feature')
-
END
-
-
1
def validate_args
-
1
if args.size > 1 || args.size == 0
-
msg = args.size > 1 ? 'Only one' : 'One'
-
msg << " argument is expected by the feature generator, e.g. 'origen new feature my_feature', 'origen new feature features/my_feature"
-
puts msg
-
exit 1
-
end
-
1
validate_args_common
-
end
-
-
1
def setup
-
1
@generate_model = false
-
1
@generate_pins = true
-
1
@generate_timesets = true
-
1
@generate_parameters = true
-
1
extract_model_name
-
1
create_files
-
1
add_acronyms
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
module CodeGenerators
-
1
class Klass < Origen::CodeGenerators::Base
-
1
def self.banner
-
'origen new class NAME'
-
end
-
-
1
desc <<-END
-
This generator creates a plain old Ruby class within your application's lib directory.
-
-
The NAME of the class should be given, in lower case, optionally indicating the presence
-
of any namespacing you want it to be created under.
-
-
Examples:
-
origen new class counter # Creates app/lib/my_application/counter.rb
-
origen new class helpers/counter # Creates app/lib/my_application/helpers/counter.rb
-
END
-
-
1
def validate_args
-
1
if args.size > 1 || args.size == 0
-
msg = args.size > 1 ? 'Only one' : 'One'
-
msg << " argument is expected by the class generator, e.g. 'origen new class counter', 'origen new class helpers/counter'"
-
puts msg
-
exit 1
-
end
-
1
validate_resource_name(args.first)
-
end
-
-
1
def create_class_file
-
1
@resource_path = args.first
-
1
klass = resource_path_to_class(args.first)
-
1
@namespaces = klass.split('::').map(&:underscore)
-
1
@name = @namespaces.pop
-
1
@namespaces = add_type_to_namespaces(@namespaces)
-
1
@root_class = true
-
1
file = class_name_to_lib_file(klass)
-
1
template 'templates/code_generators/class.rb', file
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
module CodeGenerators
-
1
class Model < Origen::CodeGenerators::Base
-
1
def self.banner
-
'origen new model NAME'
-
end
-
-
1
desc <<-END
-
This generator creates a model and optionally a controller for it within your application's
-
app/lib directory.
-
-
The NAME of the model should be given, in lower case, optionally indicating the presence
-
of any namespacing you want it to be created under.
-
-
If the model is intended to represent a top-level DUT or a primary sub-block/IP (e.g. RAM,
-
ATD, PLL, Flash, etc) then use `origen new dut` or `origen new block` instead.
-
-
If the model is intended to represent a sub-component of an existing block then the
-
block generator should be used to create a nested sub-block - see the comments within
-
sub_blocks.rb of one of the existing block models for an example.
-
-
Otherwise, models in the app/lib directory as produced by this generator are good for when
-
the model is representing some abstract concept which may not map directly to hardware, or
-
hen you need to model a minor sub-component which needs to be shared by multuple higher level
-
blocks.
-
-
Examples:
-
origen new model sequencer # Creates app/lib/my_application/sequencer.rb
-
origen new model bist/sequencer # Creates app/lib/my_application/bist/sequencer.rb
-
END
-
-
1
def validate_args
-
2
if args.size > 1 || args.size == 0
-
msg = args.size > 1 ? 'Only one' : 'One'
-
msg << " argument is expected by the model generator, e.g. 'origen new model sequencer', 'origen new model bist/sequencer'"
-
puts msg
-
exit 1
-
end
-
-
2
validate_resource_name(args.first)
-
end
-
-
1
def create_model_file
-
2
@resource_path = args.first
-
2
klass = resource_path_to_class(args.first)
-
2
@namespaces = klass.split('::').map(&:underscore)
-
2
@name = @namespaces.pop
-
2
@namespaces = add_type_to_namespaces(@namespaces)
-
2
@root_class = true
-
2
file = class_name_to_lib_file(klass)
-
2
template 'templates/code_generators/model.rb', file
-
2
if yes? 'Does this model need a controller? (n):'
-
1
file = file.to_s.sub(/\.rb/, '_controller.rb')
-
1
template 'templates/code_generators/controller.rb', file
-
end
-
2
add_acronyms
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
module CodeGenerators
-
1
class Mod < Origen::CodeGenerators::Base
-
1
def self.banner
-
'origen new module NAME [CLASS]'
-
end
-
-
1
desc <<-END
-
This generator creates a plain old Ruby module within your application's lib directory,
-
or if a CLASS argument is given, it will create it a child of that class in either the
-
lib or blocks directory as appropriate.
-
-
Where a CLASS argument is given, the new module will be automatically included in the
-
class.
-
-
The NAME of the module should be given, in lower case, optionally indicating the presence
-
of any namespacing you want it to be created under.
-
-
The CLASS argument should be a path to the Ruby file that defines the class.
-
-
Examples:
-
origen new module helpers # Creates app/lib/my_application/helpers.rb
-
origen new module helpers/math # Creates app/lib/my_application/helpers/math.rb
-
-
# Creates app/lib/blocks/dut/derivatives/falcon/model/helpers.rb
-
origen new module blocks/dut/derivatives/falcon/model.rb helpers
-
END
-
-
1
def validate_args
-
4
if args.size > 2 || args.size == 0
-
msg = args.size == 0 ? 'At least one argument is' : 'No more than two arguments are'
-
msg << " expected by the module generator, e.g. 'origen new module helpers', 'origen new module helpers app/lib/my_app/my_class.rb'"
-
puts msg
-
exit 1
-
end
-
-
4
if args.size == 2
-
4
@class_file = args.first
-
4
unless File.exist?(@class_file)
-
puts "This class file does not exist: #{@class_file}"
-
exit 1
-
end
-
end
-
-
4
@resource_path = validate_resource_path(args.last)
-
end
-
-
1
def create_module_file
-
4
if @class_file
-
4
@namespaces = resource_path_to_class(@class_file).split('::').map(&:underscore)
-
4
paths = resource_path_to_class(@resource_path).split('::').map(&:underscore)
-
4
@name = paths.pop
-
4
paths.shift # Lose the app namespace
-
4
@namespaces += paths
-
4
file = File.join(@class_file.sub('.rb', ''), "#{@name}.rb")
-
18
@module_name = (@namespaces + [@name]).map { |n| camelcase(n) }.join('::')
-
else
-
@module_name = resource_path_to_class(@resource_path)
-
@namespaces = @module_name.split('::').map(&:underscore)
-
@name = @namespaces.pop
-
file = class_name_to_lib_file(@module_name)
-
end
-
4
@namespaces = add_type_to_namespaces(@namespaces)
-
4
template 'templates/code_generators/module.rb', file
-
end
-
-
1
def include_module
-
4
if @class_file
-
4
klass = resource_path_to_class(@class_file)
-
-
# Does file have a nested namespace structure
-
4
snippet = File.foreach(@class_file).first(50)
-
83
if snippet.any? { |line| line =~ /\s*class #{klass.split('::').last}/ }
-
indent = ' ' * klass.split('::').size
-
lines = []
-
lines << indent + "include #{@module_name}"
-
lines << ''
-
inject_into_class @class_file, klass.split('::').last, lines.join("\n") + "\n"
-
-
# Else assume it is the compact style (class MyApp::DUT::Falcon)
-
else
-
4
lines = []
-
4
lines << " include #{@module_name}"
-
4
lines << ''
-
4
inject_into_class @class_file, klass, lines.join("\n") + "\n"
-
end
-
end
-
4
add_acronyms
-
end
-
end
-
end
-
end
-
1
require 'optparse'
-
1
require 'origen/commands/helpers'
-
-
1
options = {}
-
-
# App options are options that the application can supply to extend this command
-
1
app_options = @application_options || []
-
1
opt_parser = OptionParser.new do |opts|
-
1
opts.banner = <<-EOT
-
Compile an ERB template file, list of files or a directory.
-
If a directory is referenced the entire sub-directory structure will be copied to the output directory, compiling any ERB
-
templates it finds in the process and copying any standard files accross without modification.
-
-
ERB is a templating system from Ruby that allows you to embed snippets of Ruby code in any ASCII file to create dynamic
-
content. Within the context of the origen generator this means that you can include Ruby snippets that reference the target
-
objects to create dynamic test program sheets, C files, VB files, documentation, etc.
-
-
To create an ERB template start with a base ASCII file and append .erb to the end of the file name, upon compilation the
-
.erb extension will be removed.
-
-
There is not much to the syntax, this snippet covers just about everything you need to know:
-
% - A full line of Ruby is prefixed with %, this is removed by compilation
-
%# - A full line Ruby comment, this is removed by compilation
-
<%# %> - An embedded Ruby comment, this is removed by compilation
-
<% %> - An embedded Ruby snippet, this is removed by compilation
-
<%= %> - An embedded Ruby snippet that generates content, the result is output to the compiled file
-
-
Full details of ERB syntax can be found here:
-
https://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
-
-
Usage: origen compile [space separated files, lists or directories] [options]
-
EOT
-
1
opts.on('-e', '--environment NAME', String, 'Override the default environment, NAME can be a full path or a fragment of an environment file name') { |e| options[:environment] = e }
-
2
opts.on('-t', '--target NAME', String, 'Override the default target, NAME can be a full path or a fragment of a target file name') { |t| options[:target] = t }
-
2
opts.on('-pl', '--plugin PLUGIN_NAME', String, 'Set current plugin') { |pl_n| options[:current_plugin] = pl_n }
-
1
opts.on('-l', '--lsf [ACTION]', [:clear, :add], "Submit jobs to the LSF, optionally specify whether to 'clear' or 'add' to existing jobs") { |a| options[:lsf] = true; options[:lsf_action] = a }
-
1
opts.on('-w', '--wait', 'Wait for LSF processing to complete') { options[:wait_for_lsf_completion] = true }
-
1
opts.on('-c', '--continue', 'Continue on error (to the next file)') { options[:continue] = true }
-
1
opts.on('-d', '--debugger', 'Enable the debugger') { options[:debugger] = true }
-
1
opts.on('-m', '--mode MODE', Origen::Mode::MODES, 'Force the Origen operating mode:', ' ' + Origen::Mode::MODES.join(', ')) { |_m| }
-
1
opts.on('-f', '--file FILE', String, 'Override the default log file') { |o| options[:log_file] = o }
-
1
opts.on('-o', '--output DIR', String, 'Override the default output directory') { |o| options[:output] = o }
-
1
opts.on('-n', '--name NAME', String, 'Override the default output file name') { |o| options[:output_file_name] = o }
-
2
opts.on('-r', '--reference DIR', String, 'Override the default reference directory') { |o| options[:reference] = o }
-
1
opts.on('-z', '--zip', 'Gzip all output files (diff checking will be skipped)') { |o| options[:zip] = o }
-
# Apply any application option extensions to the OptionParser
-
1
Origen::CommandHelpers.extend_options(opts, app_options, options)
-
1
opts.separator ''
-
1
opts.on('-h', '--help', 'Show this message') { puts opts; exit 0 }
-
end
-
-
1
opt_parser.parse! ARGV
-
1
options[:patterns] = ARGV
-
1
options[:compile] = true # To let the generator know a compile job has been requested
-
-
1
Origen.app.plugins.temporary = options[:current_plugin] if options[:current_plugin]
-
1
Origen.environment.temporary = options[:environment] if options[:environment]
-
1
Origen.target.temporary = options[:target] if options[:target]
-
1
Origen.app.load_target!
-
1
Origen.app.runner.generate(options)
-
1
Origen.lsf.wait_for_completion if options[:wait_for_lsf_completion]
-
1
require 'optparse'
-
1
require 'origen/commands/helpers'
-
-
1
options = {}
-
-
# App options are options that the application can supply to extend this command
-
1
app_options = @application_options || []
-
1
opt_parser = OptionParser.new do |opts|
-
1
opts.banner = 'Usage: origen g [space separated patterns or lists] [options]'
-
1
opts.on('-e', '--environment NAME', String, 'Override the default environment, NAME can be a full path or a fragment of an environment file name') { |e| options[:environment] = e }
-
2
opts.on('-t', '--target NAME', String, 'Override the default target, NAME can be a full path or a fragment of a target file name') { |t| options[:target] = t }
-
1
opts.on('-l', '--lsf [ACTION]', [:clear, :add], "Submit jobs to the LSF, optionally specify whether to 'clear' or 'add' to existing jobs") { |a| options[:lsf] = true; options[:lsf_action] = a }
-
1
opts.on('-w', '--wait', 'Wait for LSF processing to complete') { options[:wait_for_lsf_completion] = true }
-
1
opts.on('-c', '--continue', 'Continue on error (to the next pattern)') { options[:continue] = true }
-
2
opts.on('-pl', '--plugin PLUGIN_NAME', String, 'Set current plugin') { |pl_n| options[:current_plugin] = pl_n }
-
1
opts.on('-f', '--file FILE', String, 'Override the default log file') { |o| options[:log_file] = o }
-
1
opts.on('-o', '--output DIR', String, 'Override the default output directory') { |o| options[:output] = o }
-
2
opts.on('-r', '--reference DIR', String, 'Override the default reference directory') { |o| options[:reference] = o }
-
1
opts.on('-q', '--queue NAME', String, 'Specify the LSF queue, default is short') { |o| options[:queue] = o }
-
1
opts.on('-p', '--project NAME', String, 'Specify the LSF project, default is msg.te') { |o| options[:project] = o }
-
1
opts.on('--doc', 'Generate into doc format') { options[:doc] = true }
-
1
opts.on('--html', 'Generate into html format') { options[:html] = true }
-
1
opts.on('--nocom', 'No comments in the generated pattern') { options[:no_comments] = true }
-
1
opts.on('-seq', '--sequence NAME', String, 'Generate multiple patterns into a single concurrent pattern sequence') { |o| options[:sequence] = o }
-
1
opts.on('-d', '--debugger', 'Enable the debugger') { options[:debugger] = true }
-
1
opts.on('-m', '--mode MODE', Origen::Mode::MODES, 'Force the Origen operating mode:', ' ' + Origen::Mode::MODES.join(', ')) { |_m| }
-
# Apply any application option extensions to the OptionParser
-
1
Origen::CommandHelpers.extend_options(opts, app_options, options)
-
1
opts.separator ''
-
1
opts.on('-h', '--help', 'Show this message') { puts opts; exit }
-
end
-
-
1
opt_parser.parse! ARGV
-
1
options[:patterns] = ARGV
-
-
1
def self._with_doc_tester(options)
-
1
if options[:doc] || options[:html]
-
Origen.app.with_doc_tester(options) do
-
yield
-
end
-
else
-
1
yield
-
end
-
end
-
-
1
Origen.load_application
-
-
1
if options[:queue]
-
Origen.config.lsf.queue = options.delete(:queue)
-
end
-
1
if options[:project]
-
Origen.config.lsf.project = options.delete(:project)
-
end
-
-
1
_with_doc_tester(options) do
-
1
Origen.app.plugins.temporary = options[:current_plugin] if options[:current_plugin]
-
1
Origen.environment.temporary = options[:environment] if options[:environment]
-
1
Origen.target.temporary = options[:target] if options[:target]
-
1
Origen.app.load_target! # This initial load is required to apply any configuration
-
# options present in the target, it will loaded again before
-
# each generate/compile job
-
1
Origen.app.runner.generate(options)
-
-
1
Origen.lsf.wait_for_completion if options[:wait_for_lsf_completion]
-
end
-
-
# method_option :vector_comments, :default => false, :aliases => "-v", :type => :boolean,
-
# :desc => "Add vector and cycle number comments to the pattern, disabled by default to make diff viewing easier"
-
-
# === Task: $ origen g [patname/patlist] [options]
-
# Generate a pattern or list of patterns.
-
#
-
# ==== Supplying a pattern name
-
# Multiple pattern name arguments can be supplied on the command line. The generator is non-strict
-
# about pre and post-fixes to the pattern names so the following all work; use whichever is most
-
# convenient:
-
#
-
# origen g prb_ers_mas_atuf
-
# origen g prb_ers_mas_atuf.rb
-
# origen g nvm_prb_ers_mas_atuf.atp
-
# origen g pattern/erase/prb_ers_mas_atuf.rb
-
#
-
# For patterns that use iterators you can supply either the pattern source name or the generated name,
-
# for example these are equivalent:
-
#
-
# origen g prb_ers_mas_bx.rb
-
# origen g prb_ers_mas_b0.atp
-
#
-
# Multiple patterns can be supplied on the command line and should be comma separated with no spaces
-
# in between:
-
#
-
# origen g prb_ers_mas_atuf,prb_ers_mas_bx
-
#
-
# A pattern list is simply the name of any file that resides in the /list directory and which contains
-
# a list of patname arguments (same rules apply as for the command line version).
-
# The list files can be commented with '#' and can reference other list files. For example it is
-
# common to make a production.list or master.list file that references other sub lists: probe.list,
-
# ft.list, etc.
-
#
-
# ==== Generator output
-
# The command line output is as follows:
-
#
-
# Generating... nvm_prb_ers_mas_atuf.atp 3039 0.201732
-
# | | |
-
# Created output file, can be found in Number of Execution time
-
# output/<soc>/ vectors on the tester
-
#
-
# All output from the last run can be found in log.txt, this is just a direct copy of the output in
-
# the console.
-
#
-
# ==== Tracking changes
-
# Upon completion you will be alerted if there are some new or changed files and will prompted to
-
# save these. It is recommended that you perform the save if you know why the change happened (or if
-
# you are running in a brand new workspace). You can then use the automatic pattern diff feature to
-
# be alerted to any change in the pattern content the next time you generate a given pattern. Note that
-
# a tkdiff executable is automatically output to allow you to inspect the differences.
-
#
-
# To allow you to locate changed or new patterns easily they are automatically copied to:
-
# output/<soc>/changed/
-
#
-
# The reference files are stored in $ORIGEN_WORK/.ref <br>
-
# Very often you will want to compare the output of the current generator to a previous version,
-
# to do this remove the .ref and replace it with a link to the .ref in another workspace.
-
# rm -fr .ref
-
# ln -s <path_to_my_ref_workspace>/.ref .ref
-
# After that generate and save the patterns in the reference workspace and re-run the patterns in the original
-
# workspace to see if there are any differences.
-
#
-
# ==== Options
-
# For a list of options run:
-
# origen help g
-
2
module Origen
-
2
module CommandHelpers
-
2
def self.extend_options(opts, app_opts, options)
-
16
app_opts.each do |app_option|
-
if app_option.last.is_a?(Proc)
-
ao_proc = app_option.pop
-
if ao_proc.arity == 1
-
opts.on(*app_option) { ao_proc.call(options) }
-
else
-
opts.on(*app_option) { |arg| ao_proc.call(options, arg) }
-
end
-
else
-
opts.on(*app_option) {}
-
end
-
end
-
end
-
end
-
end
-
2
require 'optparse'
-
2
require 'origen/commands/helpers'
-
-
2
options = {}
-
-
# App options are options that the application can supply to extend this command
-
2
app_options = @application_options || []
-
2
opt_parser = OptionParser.new do |opts|
-
2
opts.banner = 'Usage: origen p [space separated files or directories] [options]'
-
2
opts.on('-e', '--environment NAME', String, 'Override the default environment, NAME can be a full path or a fragment of an environment file name') { |e| options[:environment] = e }
-
3
opts.on('-t', '--target NAME', String, 'Override the default target, NAME can be a full path or a fragment of a target file name') { |t| options[:target] = t }
-
2
opts.on('-l', '--lsf [ACTION]', [:clear, :add], "Submit jobs to the LSF, optionally specify whether to 'clear' or 'add' to existing jobs") { |a| options[:lsf] = true; options[:lsf_action] = a }
-
2
opts.on('-w', '--wait', 'Wait for LSF processing to complete') { options[:wait_for_lsf_completion] = true }
-
2
opts.on('-c', '--continue', 'Continue on error (to the next file)') { options[:continue] = true }
-
2
opts.on('-d', '--debugger', 'Enable the debugger') { options[:debugger] = true }
-
2
opts.on('-f', '--file FILE', String, 'Override the default log file') { |o| options[:log_file] = o }
-
3
opts.on('-pl', '--plugin PLUGIN_NAME', String, 'Set current plugin') { |pl_n| options[:current_plugin] = pl_n }
-
2
opts.on('-o', '--output DIR', String, 'Override the default output directory') { |o| options[:output] = o }
-
3
opts.on('-r', '--reference DIR', String, 'Override the default reference directory') { |o| options[:reference] = o }
-
2
opts.on('--list FILE', String, 'Override the default pattern list file name') { |o| options[:referenced_pattern_list] = o }
-
2
opts.on('--doc', 'Generate into doc (yaml) format, requires a Doc interface to be setup in your application') { options[:doc] = true }
-
2
opts.on('-q', '--queue NAME', String, 'Specify the LSF queue, default is short') { |o| options[:queue] = o }
-
2
opts.on('-p', '--project NAME', String, 'Specify the LSF project, default is msg.te') { |o| options[:project] = o }
-
2
opts.on('-d', '--debugger', 'Enable the debugger') { options[:debugger] = true }
-
2
opts.on('-m', '--mode MODE', Origen::Mode::MODES, 'Force the Origen operating mode:', ' ' + Origen::Mode::MODES.join(', ')) { |_m| }
-
# Apply any application option extensions to the OptionParser
-
2
Origen::CommandHelpers.extend_options(opts, app_options, options)
-
2
opts.separator ''
-
2
opts.on('-h', '--help', 'Show this message') { puts opts; exit }
-
end
-
-
2
opt_parser.parse! ARGV
-
2
options[:files] = ARGV
-
-
2
Origen.load_application
-
-
2
if options[:queue]
-
Origen.config.lsf.queue = options.delete(:queue)
-
end
-
2
if options[:project]
-
Origen.config.lsf.project = options.delete(:project)
-
end
-
-
2
def self._with_doc_tester(options)
-
2
if options[:doc]
-
Origen.app.with_doc_tester do
-
yield
-
end
-
else
-
2
yield
-
end
-
end
-
-
2
_with_doc_tester(options) do
-
2
Origen.app.plugins.temporary = options[:current_plugin] if options[:current_plugin]
-
2
Origen.environment.temporary = options[:environment] if options[:environment]
-
2
Origen.target.temporary = options[:target] if options[:target]
-
2
Origen.app.load_target! # This initial load is required to apply any configuration
-
# options present in the target, it will loaded again before
-
# each generate/compile job
-
-
2
if Origen.config.test_program_output_directory && !options[:output]
-
options[:output] = Origen.config.test_program_output_directory
-
end
-
-
2
options[:action] = :program # Let the generator know this is a test program generation
-
2
Origen.app.runner.launch(options)
-
end
-
-
2
Origen.lsf.wait_for_completion if options[:wait_for_lsf_completion]
-
2
module Origen
-
2
module Controller
-
2
extend ActiveSupport::Concern
-
-
2
module ClassMethods
-
2
def model(options = {})
-
3
options[:controller_class] = self
-
3
if options[:path]
-
1
@path_to_model = options[:path]
-
else
-
2
options[:model_class] = _resolve_model_class(options)
-
end
-
3
Origen.controllers << options
-
end
-
-
2
def path_to_model
-
2
@path_to_model
-
end
-
-
2
def _resolve_model_class(options)
-
2
class_name = options[:class_name]
-
2
if class_name
-
2
if eval("defined? #{_namespace}::#{class_name}")
-
2
klass = eval("#{_namespace}::#{class_name}")
-
else
-
if eval("defined? #{class_name}")
-
klass = eval(class_name)
-
else
-
if eval("defined? #{self}::#{class_name}")
-
klass = eval("#{self}::#{class_name}")
-
else
-
puts "Could not find class: #{class_name}"
-
fail 'Unknown model class!'
-
end
-
end
-
end
-
2
klass
-
else
-
fail "You must supply a :class_name option when defining a controller's model!"
-
end
-
end
-
-
2
def _namespace
-
4
to_s.sub(/::[^:]*$/, '')
-
end
-
end
-
-
2
def inspect
-
2
if model
-
2
"<Model/Controller: #{model.class}:#{model.object_id}/#{self.class}:#{object_id}>"
-
else
-
"<Controller: #{self.class}:#{object_id}>"
-
end
-
end
-
-
2
def is_a?(*args)
-
171
if model
-
171
super(*args) || model.is_a?(*args)
-
else
-
super(*args)
-
end
-
end
-
-
# Returns the controller's model
-
2
def model
-
1066
@model ||= begin
-
1
if self.class.path_to_model
-
1
m = eval(self.class.path_to_model)
-
1
if m
-
1
if m.respond_to?(:_controller=)
-
m.send(:_controller=, self)
-
end
-
else
-
fail "No model object found at path: #{self.class.path_to_model}"
-
end
-
1
m
-
end
-
end
-
end
-
-
# When compared to another object, a controller will consider itself equal if either the controller
-
# or its model match the given object
-
2
def ==(obj, options = {})
-
13
if obj.is_a?(Origen::SubBlocks::Placeholder)
-
1
obj = obj.materialize
-
end
-
13
if options[:called_from_model]
-
11
super(obj)
-
else
-
2
super(obj) || model == obj
-
end
-
end
-
2
alias_method :equal?, :==
-
-
# Means that when dealing with a controller/model pair, you can
-
# always call obj.model and obj.controller to get the one you want,
-
# regardless of the one you currently have.
-
2
def controller
-
4
self
-
end
-
-
2
def respond_to?(*args)
-
1691
super || !!(!@respond_directly && model && model.respond_to_directly?(*args))
-
end
-
-
2
def respond_to_directly?(*args)
-
1482
@respond_directly = true
-
1482
result = respond_to?(*args)
-
1482
@respond_directly = false
-
1482
result
-
end
-
-
2
def to_json(*args)
-
1
model.to_json(*args)
-
end
-
-
# Used to proxy all method and attribute requests not implemented on the controller
-
# to the model.
-
#
-
# On first call of a missing method a method is generated to avoid the missing lookup
-
# next time, this should be faster for repeated lookups of the same method, e.g. reg
-
2
def method_missing(method, *args, &block)
-
151
if model.respond_to?(method)
-
# This method is handled separately since it is important to produce a proxy method
-
# that takes no arguments, otherwise the register address lookup system mistakes it
-
# for a legacy way of calculating the base address whereby the register itself was
-
# given as an argument.
-
151
if method.to_sym == :base_address
-
9
define_singleton_method(method) do
-
9
model.send(method)
-
end
-
9
base_address
-
else
-
142
define_singleton_method(method) do |*args, &block|
-
180
model.send(method, *args, &block)
-
end
-
142
send(method, *args, &block)
-
end
-
else
-
super
-
end
-
end
-
-
2
private
-
-
2
def _model=(model)
-
285
@model = model
-
end
-
end
-
end
-
2
module Origen
-
2
module Database
-
2
autoload :KeyValueStore, 'origen/database/key_value_store'
-
2
autoload :KeyValueStores, 'origen/database/key_value_stores'
-
end
-
end
-
2
module Origen
-
2
module Database
-
2
class KeyValueStore
-
2
attr_reader :name
-
# Returns the parent database (the application's collection of
-
# key-value stores)
-
2
attr_reader :database
-
2
attr_accessor :private
-
-
2
def initialize(database, name)
-
8
@name = name
-
8
@database = database
-
8
@private = false
-
end
-
-
# Read a value from the store
-
2
def [](key)
-
1771
refresh if stale?
-
1771
store[key]
-
end
-
-
# Persist a new value to the store
-
2
def []=(key, val)
-
24
refresh if persisted?
-
24
store[key] = val
-
24
save_to_file
-
24
val
-
end
-
-
# Force a refresh of the database
-
2
def refresh
-
unless @uncommitted || !persisted?
-
dssc.check_out(file, version: 'Trunk', force: true)
-
record_refresh
-
end
-
end
-
-
2
def record_refresh
-
database.record_refresh(name)
-
@store = nil
-
end
-
-
# Returns true if the database is due a time-based refresh, note that
-
# this has no bearing on whether or not someone else has committed to
-
# the store since the last refresh
-
2
def stale?
-
1771
if persisted?
-
t = database.time_since_refresh(name)
-
!t || store[:refresh_interval_in_minutes] == 0 || t > store[:refresh_interval_in_minutes]
-
else
-
1771
false
-
end
-
end
-
-
2
def persisted?
-
1912
database.persisted?
-
end
-
-
2
def private?
-
25
@private
-
end
-
-
# Check if the store has a key
-
2
def has_key?(key)
-
2
store.include? key
-
end
-
-
# Remove the session file in the case it gets corrupted
-
# This can happen when a complex object is not handled
-
# correctly by the Marshal method.
-
2
def rm_session_file
-
FileUtils.rm_f(file)
-
end
-
-
# Deletes a key from the active store
-
2
def delete_key(key)
-
1
store.delete(key)
-
1
save_to_file
-
1
load_from_file
-
end
-
-
# Return an array of store keys, excluding the 'infrastructure' key(s)
-
2
def keys
-
1
store.keys - [:refresh_interval_in_minutes]
-
end
-
-
2
private
-
-
2
def dssc
-
@dssc ||= Origen::Utility::DesignSync.new
-
end
-
-
2
def store
-
1824
@store ||= begin
-
8
if file.exist?
-
8
load_from_file
-
elsif persisted? && dssc.managed_by_design_sync?(file)
-
refresh
-
load_from_file
-
else
-
@uncommitted = true
-
{ refresh_interval_in_minutes: 60 }
-
end
-
end
-
end
-
-
2
def load_from_file
-
9
s = nil
-
9
File.open(file.to_s) do |f|
-
9
s = Marshal.load(f)
-
end
-
9
s
-
end
-
-
2
def save_to_file
-
25
unless file.dirname.exist?
-
FileUtils.mkdir_p(file.dirname.to_s)
-
end
-
25
if @uncommitted
-
database.record_new_store(name)
-
@uncommitted = false
-
end
-
25
File.open(file.to_s, 'w') do |f|
-
25
Marshal.dump(store, f)
-
end
-
25
if private?
-
FileUtils.chmod(0600, file)
-
else
-
25
FileUtils.chmod(0664, file)
-
end
-
25
if persisted?
-
dssc.check_in file, new: true, keep: true, branch: 'Trunk'
-
end
-
end
-
-
2
def file
-
92
file_path = database.app == Origen ? Origen.home : database.app.root
-
92
if persisted?
-
@file ||= Pathname.new("#{file_path}/.db/#{name.to_s.symbolize}")
-
else
-
92
@file ||= Pathname.new("#{file_path}/.session/#{name.to_s.symbolize}")
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Database
-
2
class KeyValueStores
-
# Returns the application that owns the database
-
2
attr_reader :app
-
-
2
def initialize(app, options = {})
-
options = {
-
6
persist: true
-
}.merge(options)
-
6
@app = app
-
6
@persist = options[:persist]
-
end
-
-
2
def inspect
-
if persisted?
-
app == Origen ? "Origen's Global Database" : "< #{app.class}'s Database >"
-
else
-
app == Origen ? "Origen's Global Session" : "< #{app.class}'s Session >"
-
end
-
end
-
-
# Refresh all stores
-
2
def refresh
-
if persisted?
-
_system.refresh
-
files = stores.map { |name| send(name).send(:file) }
-
dssc.check_out(files.join(' '), version: 'Trunk', force: true)
-
stores.each { |name| send(name).record_refresh }
-
end
-
nil
-
end
-
-
# Returns the time in minutes since the given store
-
# was last refreshed
-
2
def time_since_refresh(name)
-
if persisted?
-
if refresh_table[name]
-
((Time.now - refresh_table[name]) / 60).floor
-
end
-
else
-
Time.now
-
end
-
end
-
-
# Record that the given store was just refreshed
-
2
def record_refresh(name)
-
if persisted?
-
t = refresh_table
-
t[name] = Time.now
-
app.session._database[:refresh_table] = t
-
end
-
end
-
-
2
def record_new_store(name)
-
unless name == :_system || name == :_database
-
_system.refresh
-
s = stores
-
s << name unless s.include?(name)
-
_system[:stores] = s
-
end
-
end
-
-
# Used to create new key value stores on the fly.
-
#
-
# On first call of a missing method a method is generated to avoid the missing lookup
-
# next time, this should be faster for repeated lookups of the same method, e.g. reg
-
2
def method_missing(method, *args, &block)
-
7
if method.to_s =~ /(=|\(|\)|\.|\[|\]|{|}|\\|\/)/ || [:test, :_system].include?(method)
-
fail "Invalid database name: #{method}"
-
else
-
7
define_singleton_method(method) do
-
1798
loaded[method] ||= KeyValueStore.new(self, method)
-
end
-
end
-
7
send(method, *args, &block)
-
end
-
-
# Returns the names of all known stores
-
2
def stores
-
1
_system[:stores] || []
-
end
-
-
2
def persisted?
-
1912
@persist
-
end
-
-
2
def has_key?(key)
-
1
stores.include? key
-
end
-
-
2
private
-
-
2
def refresh_table
-
app.session._database[:refresh_table] ||= {}
-
end
-
-
# Persisted key value store used by the database system
-
2
def _system
-
1
@_system ||= KeyValueStore.new(self, :_system)
-
end
-
-
2
def dssc
-
@dssc ||= Origen::Utility::DesignSync.new
-
end
-
-
2
def loaded
-
1798
@loaded ||= {}
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Errata
-
2
class HwErratum
-
# ID number used to identify erratum
-
2
attr_reader :id
-
-
# Erratum Title
-
2
attr_accessor :title
-
-
# Description of erratum
-
2
attr_accessor :description
-
-
# Description of the hardware workaround for the erratum
-
2
attr_accessor :hw_workaround_description
-
-
# How the errata is to be distributed ex:
-
# --Internal Only
-
# --Customer visible
-
# --Other: 3rd party, etc.
-
2
attr_accessor :disposition
-
-
# Impact of erratum to customer
-
2
attr_accessor :impact
-
-
# When/if the erratum will be fixed
-
2
attr_accessor :fix_plan
-
-
# IP block that is associate with this errata
-
2
attr_accessor :ip_block
-
-
# Software workaround object associated with erratum
-
2
attr_accessor :sw_workaround
-
-
2
def initialize(id, ip_block, overview = {}, status = {}, sw_workaround = {})
-
3
@id = id
-
3
@ip_block = ip_block
-
3
@title = overview[:title]
-
3
@description = overview[:description]
-
3
@hw_workaround_description = overview[:hw_workaround_description]
-
3
@disposition = status[:disposition]
-
3
@impact = status[:impact]
-
3
@fix_plan = status[:fix_plan]
-
3
@sw_workaround = sw_workaround
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Errata
-
2
class SwErratumWorkaround
-
# ID number used to identify software workaround
-
2
attr_reader :id
-
-
# Title of software workaround
-
2
attr_accessor :title
-
-
# Description of software workaround and implementation
-
2
attr_accessor :description
-
-
# Availability of workaround, ex:
-
# -- Not Applicable: Errata does not affect software
-
# -- Not Available: Workaround not available
-
# -- Available: Workaround is available to be distributed
-
2
attr_accessor :sw_disposition
-
-
# Software distribution version which incorporates the workaround
-
2
attr_accessor :distribution
-
-
# Release note
-
2
attr_accessor :note
-
-
# Link to patch(s) for workaround
-
2
attr_accessor :patches
-
-
2
def initialize(id, overview = {}, resolution = {})
-
5
@id = id
-
5
@title = overview[:title]
-
5
@description = overview[:description]
-
5
@sw_disposition = overview[:sw_disposition]
-
5
@distribution = overview[:distribution]
-
5
@note = resolution[:note]
-
5
@patches = resolution[:patches]
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Features
-
2
class Feature
-
2
attr_reader :name
-
2
attr_reader :description
-
-
2
def initialize(name, options = {})
-
8
@name = name
-
8
@description = options[:description]
-
end
-
-
2
def describe
-
3
return 'No description provided!' if @description == []
-
2
if @description.class == Array
-
1
@description.join(' ')
-
else
-
1
@description
-
end
-
end
-
end
-
end
-
end
-
2
require 'pathname'
-
-
2
module Origen
-
# All logic for working with files/directories and resolving path names
-
# should be included here.
-
#
-
# An instance of this class is available as Origen.file_handler
-
#
-
# Some portions of Origen may implement local code to do this, but these
-
# should all be transitioned to use this over time.
-
2
class FileHandler
-
2
attr_accessor :default_extension
-
-
# Returns an array of file/pattern names lines from a list file.
-
# This will also take care of recursively expanding any embedded
-
# list references.
-
2
def expand_list(files, options = {})
-
options = {
-
22
preserve_duplicates: tester && tester.try(:sim?)
-
}.merge(options)
-
22
list_of_files = [files].flatten.map do |file|
-
48
f = file.strip
-
# Takes care of blank or comment lines in a list file
-
48
if f.empty? || f =~ /^\s*#/
-
8
nil
-
# Don't expand program lists when submitting to lsf,
-
# there are likely to be relational dependencies between
-
# flows meaning that they must be generated together
-
40
elsif is_a_list?(f) && !(options[:lsf] && options[:action] == :program)
-
4
expand_list(open_list(f), options)
-
else
-
36
f
-
end
-
end.flatten.compact
-
22
if options[:preserve_duplicates]
-
1
list_of_files
-
else
-
21
list_of_files.uniq
-
end
-
end
-
-
# Returns the contents of the given list file in an array, if it
-
# can be found, if not will raise an error
-
2
def open_list(file)
-
4
f = clean_path_to(file, allow_missing: true)
-
4
if f
-
3
f = File.open(f, 'r')
-
1
elsif File.exist?("#{Origen.root}/list/#{File.basename(file)}")
-
1
f = File.open("#{Origen.root}/list/#{File.basename(file)}", 'r')
-
elsif @last_opened_list_dir && File.exist?("#{@last_opened_list_dir}/#{file}")
-
f = File.open("#{@last_opened_list_dir}/#{file}", 'r')
-
else
-
fail "Could not find list file: #{file}"
-
end
-
4
lines = f.readlines
-
4
f.close
-
# Before we go save the directory of this list, this will help
-
# us to resolve any relative path references to other lists that
-
# it may contain
-
4
@last_opened_list_dir = clean_path_to(Pathname.new(f).dirname)
-
4
lines
-
end
-
-
# Returns true if the input argument is a list, for now this is
-
# simply defined by the filename ending in .list
-
2
def is_a_list?(file)
-
40
!!(file =~ /list$/)
-
end
-
-
# Yields absolute paths to the given file or directory. If a directory is supplied
-
# the method will recurse into the sub directories and ultimately yield
-
# every file contained within the directory and its children.
-
2
def resolve_files(file_or_dir_path, options = {}, &block)
-
options = {
-
# Set to :template when calling to consider references to template
-
# files from an import library
-
30
import: false
-
}.merge(options)
-
30
[file_or_dir_path].flatten.each do |file_or_dir_path|
-
30
path = inject_import_path(file_or_dir_path, type: options[:import]) if options[:import]
-
30
path = clean_path_to(file_or_dir_path, options)
-
30
self.base_directory = path unless options[:internal_call]
-
30
if path.directory?
-
5
Dir.glob("#{path}/*").sort.each do |file|
-
20
resolve_files(file, { internal_call: true }.merge(options), &block)
-
end
-
else
-
# Ignore files with the given prefix if supplied, but only if this is a file that
-
# has been found, if explicitly asked to compile a file from the caller do it regardless
-
25
if options[:ignore_with_prefix] && options[:internal_call]
-
18
return nil if path.basename.to_s =~ /^#{options[:ignore_with_prefix]}/
-
end
-
13
yield path
-
end
-
end
-
end
-
-
# Returns a full path to the given file or directory, raises an error if it
-
# can't be resolved
-
2
def clean_path_to(file, options = {})
-
# Allow individual calls to this method to specify additional custom load paths to consider
-
76
if options[:load_paths]
-
3
Array(options[:load_paths]).each do |root|
-
6
if File.exist?("#{root}/#{file}")
-
2
return Pathname.new("#{root}/#{file}")
-
end
-
end
-
end
-
74
if File.exist?(file)
-
55
if Pathname.new(file).absolute?
-
44
Pathname.new(file)
-
else
-
11
Pathname.new("#{Pathname.pwd}/#{file}")
-
end
-
# Is it a relative reference within a list file?
-
19
elsif @last_opened_list_dir && File.exist?("#{@last_opened_list_dir}/#{file}")
-
3
Pathname.new("#{@last_opened_list_dir}/#{file}")
-
# Is it a relative reference to the current base directory?
-
16
elsif File.exist?("#{base_directory}/#{file}")
-
10
Pathname.new("#{base_directory}/#{file}")
-
# Is it a path relative to Origen.root?
-
6
elsif File.exist?("#{Origen.root}/#{file}")
-
Pathname.new("#{Origen.root}/#{file}")
-
# Is it a path relative to the current directory?
-
6
elsif current_directory && File.exist?("#{current_directory}/#{file}")
-
4
Pathname.new("#{current_directory}/#{file}")
-
# Is it a path relative to the current plugin's Origen.root?
-
2
elsif Origen.app.plugins.current && File.exist?("#{Origen.app.plugins.current.root}/#{file}")
-
Pathname.new("#{Origen.app.plugins.current.root}/#{file}")
-
2
elsif options[:default_dir]
-
m = all_matches(file, options)
-
if m
-
Pathname.new(m)
-
else
-
if options[:allow_missing]
-
return nil
-
else
-
fail "Can't find: #{file}"
-
end
-
end
-
else
-
2
if options[:allow_missing]
-
return nil
-
else
-
fail "Can't find: #{file}"
-
end
-
end
-
end
-
-
2
def check(path)
-
file_plugin = Origen.app.plugins.path_within_a_plugin(path)
-
if file_plugin
-
if Origen.app.plugins.current
-
if file_plugin == Origen.app.plugins.current.name
-
return path
-
else
-
puts "The requested file is from plugin #{file_plugin} and current system plugin is set to plugin #{Origen.app.plugins.current.name}!"
-
fail 'Incorrect plugin error!'
-
end
-
else
-
Origen.app.plugins.temporary = file_plugin
-
return path
-
end
-
else
-
return path
-
end
-
end
-
-
2
def all_matches(file, options)
-
if Origen.app.plugins.current
-
matches = Dir.glob("#{options[:default_dir]}/#{Origen.app.plugins.current.name}/**/#{file}").sort
-
matches = matches.flatten.uniq
-
if matches.size == 0
-
matches = Dir.glob("#{options[:default_dir]}/**/#{file}").sort
-
matches = matches.flatten.uniq
-
end
-
else
-
matches = (Dir.glob("#{options[:default_dir]}/**/#{file}") + # Avoids symlinks
-
Dir.glob("#{options[:default_dir]}/#{file}")).sort
-
if matches.size == 0
-
matches = Dir.glob("#{options[:default_dir]}/**{,/*/**}/#{file}").sort # Takes symlinks into consideration
-
end
-
matches = matches.flatten.uniq
-
end
-
-
if matches.size == 0
-
return nil
-
elsif matches.size > 1
-
puts 'The following matches were found:'
-
puts matches
-
fail "Ambiguous file #{file}"
-
else
-
return check(matches.first)
-
end
-
end
-
-
# Returns an absolute path for the given
-
2
def relative_to_absolute(path)
-
75
if Pathname.new(path).absolute?
-
59
Pathname.new(path)
-
else
-
16
Pathname.new("#{Pathname.pwd}/#{path}")
-
end
-
end
-
-
2
def relative_path_to(path)
-
clean_path_to(path).relative_path_from(Pathname.pwd)
-
end
-
-
2
def clean_path_to_sub_template(file)
-
24
if File.exist?(file)
-
6
if Pathname.new(file).absolute?
-
6
return Pathname.new(file)
-
else
-
return Pathname.new("#{Pathname.pwd}/#{file}")
-
end
-
end
-
18
file = inject_import_path(file, type: :template)
-
18
file = add_underscore_to(file)
-
18
file = add_extension_to(file)
-
18
web_file = file =~ /\.(html|md)(\.|$)/
-
begin
-
# Allow relative references to templates/web when compiling a web template
-
18
if Origen.lsf.current_command == 'web' || web_file
-
3
clean_path_to(file, load_paths: ["#{Origen.root}/app/templates/web", "#{Origen.root}/templates/web"])
-
else
-
15
clean_path_to(file)
-
end
-
rescue
-
# Try again without .erb
-
file = file.gsub('.erb', '')
-
if Origen.lsf.current_command == 'web' || web_file
-
clean_path_to(file, load_paths: ["#{Origen.root}/app/templates/web", "#{Origen.root}/templates/web"])
-
else
-
clean_path_to(file)
-
end
-
end
-
end
-
-
2
def clean_path_to_template(file)
-
1
file = inject_import_path(file, type: :template)
-
1
file = add_extension_to(file)
-
1
clean_path_to(file)
-
end
-
-
# If the current path looks like it is a reference to an import, the
-
# path will be replaced with the absolute path to the local import directory
-
2
def inject_import_path(path, options = {})
-
37
path = path.to_s unless path.is_a?(String)
-
37
if path =~ /(.*?)\/.*/
-
24
import_name = Regexp.last_match[1].downcase.to_sym
-
24
if import_name == :origen || import_name == :origen_core || Origen.app.plugins.names.include?(import_name) ||
-
import_name == :doc_helpers
-
# Special case to allow a shortcut for this common import plugin and to also handle legacy
-
# code from when it was called doc_helpers instead of origen_doc_helpers
-
if import_name == :doc_helpers
-
root = Origen.app(:origen_doc_helpers).root
-
else
-
unless import_name == :origen || import_name == :origen_core
-
root = Origen.app(import_name).root
-
end
-
end
-
if options[:type] == :template
-
if import_name == :origen || import_name == :origen_core
-
path.sub! 'origen', "#{Origen.top}/templates/shared"
-
else
-
if File.exist?("#{root}/app/templates/shared")
-
path.sub! Regexp.last_match[1], "#{root}/app/templates/shared"
-
else
-
path.sub! Regexp.last_match[1], "#{root}/templates/shared"
-
end
-
end
-
else
-
fail 'Unknown import path type!'
-
end
-
end
-
end
-
37
path
-
end
-
-
# @deprecated
-
2
def clean_path_to_sub_program(file)
-
Origen.deprecated 'Origen.file_handler.clean_path_to_sub_program is deprecated, update to use version ... of origen_testers instead.'
-
file = add_underscore_to(file)
-
file = add_rb_to(file)
-
clean_path_to(file)
-
end
-
-
# Insert _ in file name if not present
-
2
def add_underscore_to(file)
-
30
f = Pathname.new(file)
-
30
if f.basename.to_s =~ /^_/
-
file
-
else
-
30
"#{f.dirname}/_#{f.basename}"
-
end
-
end
-
-
2
def add_rb_to(file)
-
12
f = Pathname.new(file)
-
12
"#{f.dirname}/#{f.basename('.rb')}.rb"
-
end
-
-
2
def add_extension_to(file)
-
19
f = Pathname.new(file)
-
19
if f.basename('.erb').extname.empty?
-
14
if default_extension
-
2
"#{f.dirname}/#{f.basename('.erb')}#{default_extension}.erb"
-
else
-
12
"#{f.dirname}/#{f.basename('.erb')}#{Pathname.new(Origen.file_handler.current_file).basename('.erb').extname}.erb"
-
end
-
else
-
5
"#{f.dirname}/#{f.basename('.erb')}.erb"
-
end
-
end
-
-
2
def set_output_directory(options = {})
-
options = {
-
193
create: true
-
}.merge(options)
-
193
if options[:output]
-
32
@output_directory = relative_to_absolute(options[:output])
-
else
-
161
@output_directory = Pathname.new(Origen.config.output_directory)
-
end
-
193
if options[:create]
-
193
FileUtils.mkdir_p(@output_directory) unless @output_directory.exist?
-
end
-
193
@output_directory
-
end
-
-
# Returns an absolute pathname to the current output directory
-
2
def output_directory
-
137
@output_directory ||= set_output_directory
-
end
-
-
2
def set_reference_directory(options = {})
-
options = {
-
193
create: true
-
}.merge(options)
-
193
if options[:reference]
-
43
@reference_directory = relative_to_absolute(options[:reference])
-
else
-
150
@reference_directory = Pathname.new(Origen.config.reference_directory)
-
# Create the reference output directory if it does not exist.
-
150
FileUtils.mkdir_p(@reference_directory) unless @reference_directory.exist?
-
end
-
193
if options[:create]
-
# Delete any broken symlinks in the top level .ref
-
193
dir = "#{Origen.root}/.ref"
-
193
if File.symlink?(dir)
-
FileUtils.rm_f(dir) unless File.exist?(dir)
-
end
-
193
FileUtils.mkdir_p(@reference_directory) unless @reference_directory.exist?
-
end
-
193
@reference_directory
-
end
-
-
# Returns an absolute pathname to the current reference directory
-
2
def reference_directory
-
74
@reference_directory ||= set_reference_directory
-
end
-
-
# Returns the base directory containing the source files being generated/compiled.
-
#
-
# When operating on a single file this will return the directory containing that
-
# file, when operating on a directory this will return the directory.
-
2
def base_directory
-
99
@base_directory
-
end
-
-
2
def base_directory=(file_or_dir)
-
# puts "Base directory changed by: #{caller[0]}"
-
36
if file_or_dir.directory?
-
29
@base_directory = file_or_dir
-
else
-
7
@base_directory = file_or_dir.dirname
-
end
-
end
-
-
2
def current_directory
-
16
return @current_directory if @current_directory
-
6
@current_directory = clean_path_to(current_file).dirname if current_file
-
end
-
-
2
def current_file=(file)
-
147
@current_directory = nil
-
147
@current_file = file
-
end
-
-
2
def current_file
-
526
@current_file
-
end
-
-
2
def preserve_current_file
-
2
file = current_file
-
2
yield
-
2
self.current_file = file
-
end
-
-
2
def preserve_state
-
28
file = current_file
-
28
dir = base_directory
-
28
output = output_directory
-
28
ref = reference_directory
-
28
ext = default_extension
-
28
yield
-
28
self.base_directory = dir if dir
-
28
self.current_file = file if file
-
28
set_output_directory(output: output) if output
-
28
set_reference_directory(reference: ref) if ref
-
28
self.default_extension = ext
-
end
-
-
2
def preserve_and_clear_state
-
file = current_file
-
dir = base_directory
-
output = output_directory
-
ref = reference_directory
-
ext = default_extension
-
current_file = nil
-
base_directory = nil
-
output_directory = nil
-
reference_directory = nil
-
yield
-
self.base_directory = dir if dir
-
self.current_file = file if file
-
set_output_directory(output: output) if output
-
set_reference_directory(reference: ref) if ref
-
self.default_extension = ext
-
end
-
-
# Returns the sub directory of the current base directory that the
-
# given file is in
-
2
def sub_dir_of(file, base = base_directory)
-
36
file = Pathname.new(file) unless file.respond_to?(:relative_path_from)
-
36
base = Pathname.new(base) unless base.respond_to?(:relative_path_from)
-
36
rel = file.relative_path_from(base)
-
36
if file.directory?
-
rel
-
else
-
36
rel.dirname
-
end
-
end
-
-
# Convenience method to use when you want to write to a file, this takes
-
# care of ensuring that the directory exists prior to attempting to open
-
# the file
-
2
def open_for_write(path)
-
dir = Pathname.new(path).dirname
-
FileUtils.mkdir_p(dir) unless File.exist?(dir)
-
File.open(path, 'w') do |f|
-
yield f
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
class Generator
-
2
class Compiler # :nodoc: all
-
2
require 'fileutils'
-
2
require 'erb'
-
2
require 'pathname'
-
2
require "#{Origen.top}/helpers/url"
-
-
2
include Helpers
-
2
include Comparator
-
2
include Renderer
-
-
# During a compile this will return the current top-level file being compiled
-
#
-
# @example
-
# Origen.generator.compiler.current_file # => Pathname
-
2
attr_reader :current_file
-
-
# Where compile will place the compiled content in an output file, this method will return
-
# it as a string to the caller (i.e. without creating an output file)
-
#
-
# It expects an absolute path to a single template file as the file argument.
-
#
-
# @api private
-
2
def compile_inline(file, options = {})
-
11
initial_options = options.merge({})
-
options = {
-
11
check_for_changes: false,
-
sub_template: false,
-
collect_stats: false,
-
initial_options: initial_options
-
}.merge(options)
-
11
@scope = options[:scope]
-
11
file = Pathname.new(file) unless options[:string]
-
11
run_erb(file, options).strip
-
end
-
-
# Compile all files found under the source directory, non-erb files will be copied
-
# to the destination un-altered
-
2
def compile(file_or_dir, options = {})
-
options = {
-
31
check_for_changes: true,
-
sub_template: false,
-
collect_stats: true
-
}.merge(options)
-
31
@scope = options[:scope]
-
# Doing here so the output_directory (requiring target load) doesn't get hit if
-
# it is already defined
-
31
options[:output_directory] ||= output_directory
-
31
@check_for_changes = options[:check_for_changes]
-
31
@options = options
-
31
if options[:sub_template]
-
23
block = options.delete(:block)
-
23
if is_erb?(file_or_dir)
-
23
run_erb(file_or_dir, options, &block)
-
else
-
f = File.open(file_or_dir)
-
content = f.read
-
f.close
-
insert(content)
-
end
-
else
-
8
Origen.file_handler.resolve_files(file_or_dir, ignore_with_prefix: '_', import: :template) do |file|
-
9
compile_file(file, options)
-
end
-
end
-
end
-
-
2
def merge(file_or_dir, options = {})
-
# Compile an up to date reference
-
compile(file_or_dir_path, check_for_changes: false, output_directory: merge_reference_directory)
-
diffs = []
-
Origen.file_handler.resolve_files(file_or_dir, ignore_with_prefix: '_') do |file|
-
diffs << merge_file(file, options)
-
end
-
diffs.compact!
-
puts ''
-
if diffs.size > 0
-
puts 'The following differences are present in the compiled files and must be resolved manually:'
-
puts ''
-
diffs.each do |diff|
-
puts diff
-
end
-
puts ''
-
else
-
puts 'Merged successfully!'
-
end
-
end
-
-
2
def stats
-
10
Origen.app.stats
-
end
-
-
# Compile the supplied file if it is an erb template writing the compiled
-
# version to the destination directory.
-
# If the file is not an erb template it is simply copied un-altered to the
-
# destination directory.
-
# File must be an absolute path to the file.
-
2
def compile_file(file, options = {})
-
9
@current_file = Pathname.new(file)
-
# This is used when templates are compiled through a test program, but can
-
# be problematic when used to compile files standalone. In practice this may
-
# not be an issue except when testing Origen and generating and compiling within
-
# the same thread, but clearing this here doesn't seem to do any harm.
-
9
Origen.file_handler.default_extension = nil
-
9
Origen.log.info "Compiling... #{relative_path_to(file)}" unless options[:quiet]
-
9
Origen.log.info " Created... #{relative_path_to(output_file(file, options))}" unless options[:quiet]
-
9
stats.completed_files += 1 if options[:collect_stats]
-
9
if is_erb?(file)
-
8
output = run_erb(file, options)
-
8
f = output_file(file, options).to_s
-
8
if output.is_a?(Pathname)
-
FileUtils.mv output.to_s, f
-
else
-
16
File.open(f, 'w') { |out| out.puts output }
-
end
-
else # Just copy it across
-
1
out = output_file(file, options)
-
# Delete the target if it already exists, this prevents permission denied errors when copying
-
1
FileUtils.rm_f(out.to_s) if File.exist?(out.to_s)
-
1
FileUtils.cp(file.to_s, out.dirname.to_s)
-
end
-
9
if options[:zip]
-
`gzip -f -9 #{output_file(file, options)}`
-
else
-
9
if @check_for_changes
-
9
check_for_changes(output_file(file, options), reference_file(file, options),
-
9
comment_char: Origen.app.tester ? Origen.app.tester.program_comment_char : nil,
-
compile_job: true)
-
end
-
end
-
end
-
-
2
def run_erb(file, opts = {}, &block)
-
# Refresh the target to start all settings from scratch each time
-
# This is an easy way to reset all registered values
-
42
if opts[:preserve_target]
-
1
options[:preserve_target] = opts.delete(:preserve_target)
-
end
-
42
Origen.app.reload_target! unless options[:preserve_target]
-
# Record the current file, this can be used to resolve any relative path
-
# references in the file about to be compiled
-
42
Origen.file_handler.current_file = file
-
# Make the file and options available to the template
-
42
if opts[:initial_options] || opts[:options]
-
12
options.merge!(opts.delete(:initial_options) || opts.delete(:options))
-
end
-
42
options[:file] = file
-
42
options[:top_level_file] = current_file
-
42
b = _get_binding(opts, &block)
-
42
if opts[:string]
-
1
content = file
-
1
@current_buffer = '@_string_template'
-
1
buffer = @current_buffer
-
else
-
41
content = File.read(file.to_s)
-
41
buffer = buffer_name_for(file)
-
end
-
42
if block_given?
-
5
content = ERB.new(content, 0, '%<>', buffer).result(b)
-
else
-
37
content = ERB.new(content, 0, Origen.config.erb_trim_mode, buffer).result(b)
-
end
-
42
insert(content)
-
end
-
-
# @api private
-
2
def _get_binding(opts, &block)
-
# Important, don't declare any local variable called options here,
-
# the scope of this method will be the default for any templates and
-
# we want options to refer to the global options method
-
42
b = opts[:binding] || opts[:scope] || binding
-
# If an object has been supplied as the scope, then do some tricks
-
# to get a hold of its internal scope
-
42
unless b.is_a?(Binding)
-
5
b.define_singleton_method :_get_binding do |local_opts, &_block|
-
# rubocop:disable Lint/UselessAssignment
-
5
options = local_opts
-
# rubocop:enable Lint/UselessAssignment
-
5
binding
-
end
-
# Here the global options, the ones visible right now, are passed to into the method defined above,
-
# they will get assigned to the local variable called option and that is what the template will
-
# be able to see
-
5
b = b._get_binding(options, &block)
-
end
-
42
b
-
end
-
-
2
def current_buffer
-
5
(@scope || self).instance_variable_get(@current_buffer || '@_anonymous')
-
end
-
-
2
def current_buffer=(text)
-
28
(@scope || self).instance_variable_set(@current_buffer || '@_anonymous', text)
-
end
-
-
# Returns the ERB buffer name for the given file, something like "@my_file_name"
-
2
def buffer_name_for(file)
-
41
expected_filename = file.basename.to_s.chomp('.erb')
-
41
expected_filename.gsub!('-', '_') if expected_filename.match(/-/)
-
41
expected_filename.gsub!('.', '_') if expected_filename.match(/./)
-
41
@current_buffer = '@_' + expected_filename
-
end
-
-
2
def merge_file(file, _options = {})
-
file = Pathname.new(file)
-
Origen.log.info "Merging... #{file.basename}"
-
if is_erb?(file) && File.exist?(output_file(file))
-
check_for_differences(output_file(file), merge_ref_file(file), file)
-
elsif File.exist?(output_file(file))
-
if check_for_differences(output_file(file), merge_ref_file(file), file)
-
FileUtils.cp(output_file(file), file.dirname.to_s)
-
end
-
end
-
end
-
-
2
def display_path_to(file)
-
p = relative_path_to(file).to_s
-
p.gsub!('/', '\\') if Origen.running_on_windows?
-
p
-
end
-
-
2
def check_for_differences(a, b, file)
-
if check_for_changes(a, b, comment_char: ["'", 'logprint'], quiet: true, compile_job: true)
-
puts "*** CHANGE DETECTED *** To rollback: #{Origen.config.copy_command} #{display_path_to(b)} #{display_path_to(a)}"
-
"#{Origen.config.diff_command} #{display_path_to(a)} #{display_path_to(b)} & #{ENV['EDITOR']} #{file.cleanpath} &"
-
end
-
end
-
-
# Returns true if the supplied file name has a .erb extension
-
2
def is_erb?(file)
-
32
!!(file.to_s =~ /.erb$/) || !Origen.config.compile_only_dot_erb_files
-
end
-
-
2
def output_directory
-
58
Origen.file_handler.output_directory
-
end
-
-
2
def reference_directory
-
9
Origen.file_handler.reference_directory
-
end
-
-
2
def merge_reference_directory
-
"#{Origen.root}/.merge_ref"
-
end
-
-
# Returns the output file corresponding to the given source file, the destination
-
# directory will be created if it doesn't exist.
-
2
def output_file(file, options = {})
-
options = {
-
27
output_directory: output_directory
-
}.merge(options)
-
# return @output_file if @output_file
-
27
sub_dir = options[:output_sub_dir] || Origen.file_handler.sub_dir_of(file).to_s
-
27
sub_dir = nil if sub_dir == '.'
-
27
filename = options[:output_file_name] || file.basename.to_s.gsub('.erb', '')
-
# filename.gsub!('target', $target.id) if filename =~ /target/ && $target.id
-
27
output = Pathname.new("#{options[:output_directory]}#{sub_dir ? '/' + sub_dir : ''}/#{filename}")
-
27
FileUtils.mkdir_p(output.dirname.to_s) unless File.exist?(output.dirname.to_s)
-
# @output_file = output
-
27
output
-
end
-
-
# Returns the reference file corresponding to the given source file, the destination
-
# directory will be created if it doesn't exist.
-
2
def reference_file(file, options = {})
-
# return @reference_file if @reference_file
-
9
sub_dir = Origen.file_handler.sub_dir_of(file).to_s
-
9
sub_dir = nil if sub_dir == '.'
-
9
filename = options[:output_file_name] || file.basename.to_s.gsub('.erb', '')
-
# filename.gsub!('target', $target.id) if filename =~ /target/ && $target.id
-
9
reference = Pathname.new("#{reference_directory}#{sub_dir ? '/' + sub_dir : ''}/#{filename}")
-
9
FileUtils.mkdir_p(reference.dirname.to_s) unless File.exist?(reference.dirname.to_s)
-
# @reference_file = reference
-
9
reference
-
end
-
-
2
def merge_ref_file(file, options = {})
-
options = {
-
directory: merge_reference_directory
-
}.merge(options)
-
# return @merge_ref_file if @merge_ref_file
-
sub_dir = Origen.file_handler.sub_dir_of(file).to_s
-
sub_dir = nil if sub_dir == '.'
-
filename = file.basename.to_s.gsub('.erb', '')
-
# filename.gsub!('target', $target.id) if filename =~ /target/ && $target.id
-
output = Pathname.new("#{options[:directory]}#{sub_dir ? '/' + sub_dir : ''}/#{filename}")
-
FileUtils.mkdir_p(output.dirname.to_s) unless File.exist?(output.dirname.to_s)
-
# @merge_ref_file = output
-
output
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
class Generator
-
# A job is responsible for executing a single pattern source
-
2
class Job # :nodoc: all
-
2
attr_accessor :output_file_body, :pattern
-
2
attr_reader :split_counter, :split_names
-
2
attr_reader :options
-
-
2
def initialize(pattern, options)
-
108
@testing = options[:testing]
-
108
@options = options
-
108
@requested_pattern = pattern
-
108
@no_comments = options[:no_comments]
-
108
@output_opt = options[:output]
-
end
-
-
# Returns true if the job is a test job, will only be true in a test scenario
-
2
def test?
-
186
@testing
-
end
-
-
2
def no_comments?
-
@no_comments
-
end
-
-
2
def inc_split_counter(name = '')
-
4
@split_counter ||= 0
-
4
@split_names ||= ['']
-
4
@split_counter += 1
-
4
@split_names << name
-
end
-
-
2
def requested_pattern
-
93
@requested_pattern
-
end
-
2
alias_method :requested_file, :requested_pattern
-
-
# Returns a full path to the output pattern, note that this is not available
-
# until the job has been run
-
2
def output_pattern
-
878
"#{output_pattern_directory}/#{output_pattern_filename}"
-
end
-
2
alias_method :output_file, :output_pattern
-
-
2
def reference_pattern
-
86
"#{reference_pattern_directory}/#{output_pattern_filename}"
-
end
-
2
alias_method :reference_file, :reference_pattern
-
-
2
def output_pattern_filename
-
1386
return '' if @testing
-
# If the pattern name has been overridden by an interator use that
-
1358
return @output_pattern_filename if @output_pattern_filename
-
432
if !@pattern && !@output_file_body
-
fail 'Sorry the output_pattern is not available until the job has been run'
-
end
-
432
body = @output_file_body ? @output_file_body : File.basename(@pattern, '.rb')
-
432
output_prefix + body + output_postfix + split_number + output_extension
-
end
-
-
# This can be modified at runtime by the pattern generator in response to
-
# iterator substitutions
-
2
def output_pattern_filename=(val)
-
150
@output_pattern_filename = val
-
end
-
-
2
def reset_output_pattern_filename
-
85
@output_pattern_filename = nil
-
end
-
-
2
def output_pattern_directory
-
964
@output_pattern_directory ||= begin
-
40
dir = output_override || Origen.app.config.pattern_output_directory
-
40
if tester.respond_to?(:subdirectory)
-
dir = File.join(dir, tester.subdirectory)
-
end
-
40
FileUtils.mkdir_p(dir) unless File.exist?(dir)
-
40
dir
-
end
-
end
-
-
2
def reference_pattern_directory
-
86
@reference_pattern_directory ||= begin
-
26
dir = Origen.file_handler.reference_directory
-
26
if tester.respond_to?(:subdirectory)
-
dir = File.join(dir, tester.subdirectory)
-
end
-
26
FileUtils.mkdir_p(dir) unless File.exist?(dir)
-
26
dir
-
end
-
end
-
-
2
def output_prefix
-
432
p = Origen.config.pattern_prefix ? Origen.config.pattern_prefix + '_' : ''
-
432
p = "_#{p}" if Origen.tester.doc?
-
432
p
-
end
-
-
2
def output_postfix
-
492
Origen.config.pattern_postfix ? '_' + Origen.config.pattern_postfix : ''
-
end
-
-
2
def output_extension
-
585
'.' + Origen.tester.pat_extension
-
end
-
-
2
def output_override
-
40
if @output_opt
-
10
if @output_opt =~ /#{Origen.root}/
-
2
return @output_opt
-
else
-
8
return "#{Origen.root}/#{@output_opt}"
-
end
-
end
-
nil
-
end
-
-
2
def split_number
-
432
if split_counter
-
52
if split_names[split_counter] != ''
-
26
"_#{split_names[split_counter]}"
-
else
-
26
"_part#{split_counter}"
-
end
-
else
-
380
''
-
end
-
end
-
-
2
def strip_dir_and_ext(name)
-
38
Pathname.new(name).basename('.*').basename('.*').to_s
-
end
-
-
2
def run
-
38
Origen.app.current_jobs << self
-
begin
-
38
if @options[:compile]
-
8
Origen.log.start_job(strip_dir_and_ext(@requested_pattern), :compiler)
-
8
Origen.generator.compiler.compile(@requested_pattern, @options)
-
30
elsif @options[:job_type] == :merge
-
Origen.log.start_job(strip_dir_and_ext(@requested_pattern), :merger)
-
Origen.generator.compiler.merge(@requested_pattern)
-
30
elsif @options[:action] == :program
-
4
if Origen.running_simulation?
-
Origen.log.start_job(strip_dir_and_ext(@requested_pattern), :simulator)
-
else
-
4
Origen.log.start_job(strip_dir_and_ext(@requested_pattern), :program_generator)
-
end
-
4
Origen.flow.reset
-
4
Origen.resources.reset
-
4
OrigenTesters::Generator.execute_source(@pattern)
-
else
-
26
if Origen.running_simulation?
-
Origen.log.start_job(strip_dir_and_ext(@requested_pattern), :simulator)
-
else
-
26
Origen.log.start_job(strip_dir_and_ext(@requested_pattern), :pattern_generator)
-
end
-
26
Origen.generator.pattern.reset # Resets the pattern controller ready for a new pattern
-
# Give the app a chance to handle pattern dispatch
-
26
skip = false
-
26
Origen.app.listeners_for(:before_pattern_lookup).each do |listener|
-
26
skip ||= !listener.before_pattern_lookup(@requested_pattern)
-
end
-
26
unless skip
-
26
if @options[:sequence]
-
1
@pattern = @requested_pattern
-
1
Origen.pattern.sequence do |seq|
-
# This splits the pattern name by "_" then removes all values that are common to all patterns
-
# and then rejoins what is left.
-
# The goal is to keep the thread ID concise for the log and rather than using the whole pattern
-
# name only focussing on what is different.
-
# e.g. if you combined patterns flash_read_ckbd_ip1_max.rb and flash_read_ckbd_ip2_max.rb into
-
# a concurrent sequence then the two threads would be called 'ip1' and 'ip2'.
-
1
ids = @options[:patterns].map do |pat|
-
2
Pathname.new(pat).basename('.*').to_s.split('_')
-
end
-
14
ids = ids.map { |id| id.reject { |i| ids.all? { |id| id.include?(i) } }.join('_') }
-
-
1
@options[:patterns].each_with_index do |pat, i|
-
2
id = ids[i]
-
2
id = i.to_s if id.empty?
-
2
seq.in_parallel id do
-
2
seq.run pat
-
end
-
end
-
end
-
else
-
25
@pattern = Origen.generator.pattern_finder.find(@requested_pattern, @options)
-
25
if @pattern.is_a?(Hash)
-
1
@output_file_body = @pattern[:output]
-
1
@pattern = @pattern[:pattern]
-
end
-
25
load @pattern unless @pattern == :skip # Run the pattern
-
end
-
end
-
end
-
rescue Exception => e
-
# Whoever has aborted the job is responsible for cleaning it up
-
unless e.is_a?(Origen::Generator::AbortError)
-
if @options[:continue] || Origen.running_remotely?
-
Origen.log.error "FAILED - #{@requested_pattern} (for target #{Origen.target.name})"
-
Origen.log.error e.message
-
e.backtrace.each do |l|
-
Origen.log.error l
-
end
-
if @options[:compile]
-
Origen.app.stats.failed_files += 1
-
else
-
Origen.app.stats.failed_patterns += 1
-
end
-
else
-
raise
-
end
-
end
-
end
-
38
Origen.log.stop_job
-
38
Origen.app.current_jobs.pop
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
class Generator
-
# The pattern finder is responsible for finding patterns in the pattern
-
# directory, allowing the user to create any number of pattern files
-
# in any number of sub directories without having to declare them.
-
2
class PatternFinder
-
2
def find(name, options)
-
# If the pattern is a fully qualified path to a Ruby file, then just run that:
-
27
if File.exist?(name) && name.strip =~ /\.rb$/
-
2
return check(name, options)
-
end
-
-
25
name = File.basename(name)
-
25
@requested_pattern = name # Remember what was originally asked for in case
-
# it needs to be output in an error message
-
-
# Strip the prefix if exists
-
25
if Origen.config.pattern_prefix && name =~ /^#{Origen.config.pattern_prefix}_/
-
name.gsub!(/^#{Origen.config.pattern_prefix}_/, '')
-
end
-
-
# Strip the extension if present
-
25
name.gsub!(/\.\w+$/, '')
-
-
# Strip the postfix if exists
-
25
if Origen.config.pattern_postfix && name =~ /_#{Origen.config.pattern_postfix}$/
-
name.gsub!(/_#{Origen.config.pattern_postfix}$/, '')
-
end
-
-
# Otherwise see what can be found...
-
25
return :skip unless proceed_with_pattern?(name) # The application has elected not to run this pattern
-
-
25
pats = matching_patterns(name)
-
# If the pattern is not found in current plugin and current app then look into other included plugins as well
-
25
if pats.size == 0
-
1
pats = all_matches(name)
-
end
-
-
25
if pats.size == 0
-
# If a pattern can't be found see if it is because the real pattern name is actually
-
# a substituted value.
-
# Don't want to do this up front since it is possible that some patterns
-
# will actually have an explicit value in the name.
-
1
translation = Origen.config.pattern_name_translator(name)
-
# Give the current plugin a go at translating the name if the current application
-
# has not modified it
-
1
if translation == name && Origen.app.plugins.current
-
translation = Origen.app.plugins.current.config.pattern_name_translator(name)
-
end
-
1
if translation
-
1
if translation.is_a?(Hash)
-
1
name = translation[:source]
-
else
-
name = translation
-
translation = nil
-
end
-
end
-
1
return :skip unless proceed_with_pattern?(name) # The application has elected not to run this pattern
-
1
pats = matching_patterns(name)
-
1
if pats.size == 0
-
pats = all_matches(name)
-
end
-
end
-
-
# Last chance see if the supplied name works, this could happen if the user normally
-
# substitutes the name in before_pattern but here they have a pattern that
-
# actually includes the bit that is normally sub'd out
-
25
if pats.size == 0
-
pats = matching_patterns(@requested_pattern)
-
if pats.size == 0
-
pats = all_matches(@requested_pattern)
-
end
-
end
-
-
25
if pats.size == 0
-
fail "Can't find: #{@requested_pattern}"
-
25
elsif pats.size > 1
-
ambiguous_error(pats)
-
else
-
-
25
if translation
-
1
translation.merge(pattern: check(pats.first, options))
-
else
-
24
check(pats.first, options)
-
end
-
end
-
end
-
-
2
def matching_patterns(name)
-
# Remove extension in case it is something else, e.g. .atp
-
26
name = name.gsub(/\..*$/, '')
-
26
matches = []
-
# First look into the current plugin
-
26
if current_plugin_pattern_path
-
16
matches = Dir.glob("#{current_plugin_pattern_path}/**/#{name}.rb").sort
-
# If the current plugin does not include the pattern then look into the current app
-
16
if matches.size == 0
-
14
matches = Dir.glob(["#{pattern_directory}/**/#{name}.rb", "#{Origen.root}/app/patterns/**/#{name}.rb"]).sort # <= this does not include symlinks
-
end
-
else
-
10
matches = Dir.glob(["#{pattern_directory}/**/#{name}.rb", "#{Origen.root}/app/patterns/**/#{name}.rb"]).sort # <= this does not include symlinks
-
end
-
-
26
matches
-
end
-
-
2
def current_plugin_pattern_path
-
42
cp = Origen.app.plugins.current
-
42
if cp && cp.config.shared
-
32
path = cp.config.shared[:patterns] || cp.config.shared[:pattern]
-
32
File.join(cp.root, path) if path
-
end
-
end
-
-
2
def all_matches(name)
-
1
name = name.gsub(/\..*$/, '')
-
1
matches = Dir.glob(["#{pattern_directory}/**{,/*/**}/#{name}.rb", "#{Origen.root}/app/patterns/**{,/*/**}/#{name}.rb"]).sort # Takes symlinks into consideration
-
1
matches.flatten.uniq
-
end
-
-
2
def pattern_directory
-
25
Origen.config.pattern_directory
-
end
-
-
# Check with the application that it wishes to run the given pattern
-
2
def proceed_with_pattern?(name)
-
53
Origen.config.proceed_with_pattern(name)
-
end
-
-
2
def check(path, options = {})
-
27
file_plugin = Origen.app.plugins.plugin_name_from_path(path)
-
27
if file_plugin
-
4
if Origen.app.plugins.current
-
4
if file_plugin == Origen.app.plugins.current.name
-
4
return proceed_with_pattern?(path) ? path : :skip
-
elsif !options[:current_plugin]
-
Origen.app.plugins.current.temporary = file_plugin
-
return proceed_with_pattern?(path) ? path : :skip
-
else
-
puts "The requested pattern is from plugin #{file_plugin} and current system plugin is set to plugin #{Origen.app.plugins.current.name}!"
-
fail 'Incorrect plugin error!'
-
end
-
else
-
Origen.app.plugins.current.temporary = file_plugin
-
return proceed_with_pattern?(path) ? path : :skip
-
end
-
else
-
23
return proceed_with_pattern?(path) ? path : :skip
-
end
-
end
-
-
2
def ambiguous_error(pats)
-
if Origen.running_locally?
-
Origen.log.info 'The following patterns match:'
-
Origen.log.info pats
-
end
-
fail "Ambiguous name: #{@requested_pattern}"
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
class Generator
-
2
class PatternIterator
-
2
attr_accessor :key
-
-
2
def invoke(options, &block)
-
16
if enabled?(options)
-
16
@loop.call(options[key], &block)
-
else
-
yield
-
end
-
end
-
-
2
def loop(&block)
-
6
@loop = block
-
end
-
-
2
def setup(&block)
-
122
if block
-
2
@setup = block
-
120
elsif @setup
-
60
@setup
-
# Setup is optional for an iterator, return something to keep the caller happy
-
else
-
120
->(arg) { arg }
-
end
-
end
-
-
2
def startup(&block)
-
120
if block
-
@startup = block
-
120
elsif @startup
-
@startup
-
# Startup is optional for an iterator, return something to keep the caller happy
-
else
-
240
->(_options, arg) { arg }
-
end
-
end
-
-
2
def pattern_name(&block)
-
126
if block
-
6
@pattern_name = block
-
120
elsif @pattern_name
-
120
@pattern_name
-
else
-
fail "pattern_name must be defined for iterator: #{key}"
-
end
-
end
-
-
2
def enabled?(options)
-
376
options.keys.include?(key)
-
end
-
end
-
end
-
end
-
1
require 'io/console'
-
-
1
module Origen
-
1
class Generator
-
# Manages a single pattern sequence, i.e. an instance of PatternSequence is
-
# created for every Pattern.sequence do ... end block
-
1
class PatternSequence
-
1
def initialize(name, block)
-
4
@number_of_threads = 1
-
4
@name = name
-
4
@running_thread_ids = { main: true }
-
# The contents of the main Pattern.sequence block will be executed as a thread and treated
-
# like any other parallel block
-
4
thread = PatternThread.new(:main, self, block, true)
-
4
threads << thread
-
4
active_threads << thread
-
end
-
-
# Execute the given pattern
-
1
def run(pattern_name)
-
2
name = Pathname.new(pattern_name.to_s).basename
-
2
ss "START OF PATTERN: #{name}"
-
# Give the app a chance to handle pattern dispatch
-
2
skip = false
-
2
Origen.app.listeners_for(:before_pattern_lookup).each do |listener|
-
2
skip ||= !listener.before_pattern_lookup(pattern_name.to_s)
-
end
-
2
unless skip
-
2
pattern = Origen.generator.pattern_finder.find(pattern_name.to_s, {})
-
2
pattern = pattern[:pattern] if pattern.is_a?(Hash)
-
2
load pattern
-
end
-
2
ss "END OF PATTERN: #{name}"
-
end
-
1
alias_method :call, :run
-
-
# Execute the given block in a new concurrent thread
-
1
def thread(id = nil, &block)
-
8
@number_of_threads += 1
-
8
id ||= "thread#{@number_of_threads}".to_sym
-
# Just stage the request for now, it will be started at the end of the current execute loop
-
8
@parallel_blocks_waiting_to_start ||= []
-
8
@parallel_blocks_waiting_to_start << [id, block]
-
8
@running_thread_ids[id] = true
-
end
-
1
alias_method :in_parallel, :thread
-
-
1
def wait_for_threads(*ids)
-
3
completed = false
-
3
blocked = false
-
3
ids = ids.map(&:to_sym)
-
3
all = ids.empty? || ids.include?(:all)
-
3
until completed
-
22
if all
-
19
limit = current_thread.id == :main ? 1 : 2
-
19
if @running_thread_ids.size > limit
-
17
current_thread.waiting_for_thread(blocked)
-
17
blocked = true
-
else
-
2
current_thread.record_active if blocked
-
2
completed = true
-
end
-
else
-
6
if ids.any? { |id| @running_thread_ids[id] }
-
2
current_thread.waiting_for_thread(blocked)
-
2
blocked = true
-
else
-
1
current_thread.record_active if blocked
-
1
completed = true
-
end
-
end
-
end
-
end
-
1
alias_method :wait_for_thread, :wait_for_threads
-
-
1
private
-
-
1
def thread_running?(id)
-
@running_thread_ids[id]
-
end
-
-
1
def current_thread
-
41
PatSeq.thread
-
end
-
-
1
def log_execution_profile
-
4
if threads.size > 1
-
16
thread_id_size = threads.map { |t| t.id.to_s.size }.max
-
4
line_size = IO.console.winsize[1] - 35 - thread_id_size
-
4
line_size -= 16 if tester.try(:sim?)
-
4
cycles_per_tick = (@cycle_count_stop / (line_size * 1.0)).ceil
-
4
if tester.try(:sim?)
-
execution_time = tester.execution_time_in_ns / 1_000_000_000.0
-
else
-
4
execution_time = Origen.app.stats.execution_time_for(Origen.app.current_job.output_pattern)
-
end
-
4
Origen.log.info ''
-
4
tick_time = execution_time / line_size
-
-
4
Origen.log.info "Concurrent execution profile (#{pretty_time(tick_time)}/increment):"
-
4
Origen.log.info
-
-
4
number_of_ticks = @cycle_count_stop / cycles_per_tick
-
-
4
ticks_per_step = 0
-
4
step_size = 0.1.us
-
-
4
while ticks_per_step < 10
-
19
step_size = step_size * 10
-
19
ticks_per_step = step_size / tick_time
-
end
-
-
4
ticks_per_step = ticks_per_step.ceil
-
4
step_size = tick_time * ticks_per_step
-
-
4
if tester.try(:sim?)
-
padding = '.' + (' ' * (thread_id_size + 1))
-
else
-
4
padding = ' ' * (thread_id_size + 2)
-
end
-
4
scale_step = '|' + ('-' * (ticks_per_step - 1))
-
4
number_of_steps = (number_of_ticks / ticks_per_step) + 1
-
4
scale = scale_step * number_of_steps
-
4
scale = scale[0, number_of_ticks]
-
4
Origen.log.info padding + scale
-
-
4
scale = ''
-
4
number_of_steps.times do |i|
-
18
scale += pretty_time(i * step_size, 1).ljust(ticks_per_step)
-
end
-
4
scale = scale[0, number_of_ticks]
-
4
Origen.log.info padding + scale
-
-
4
threads.each do |thread|
-
12
line = thread.execution_profile(0, @cycle_count_stop, cycles_per_tick)
-
12
Origen.log.info ''
-
12
Origen.log.info "#{thread.id}: ".ljust(thread_id_size + 2) + line
-
end
-
4
Origen.log.info ''
-
end
-
end
-
-
1
def pretty_time(time, number_decimal_places = 0)
-
22
return '0' if time == 0
-
18
if time < 1.us
-
"%.#{number_decimal_places}fns" % (time * 1_000_000_000)
-
18
elsif time < 1.ms
-
4
"%.#{number_decimal_places}fus" % (time * 1_000_000)
-
14
elsif time < 1.s
-
14
"%.#{number_decimal_places}fms" % (time * 1_000)
-
else
-
"%.#{number_decimal_places}fs" % tick_time
-
end
-
end
-
-
1
def thread_completed(thread)
-
12
@running_thread_ids.delete(thread.id)
-
12
active_threads.delete(thread)
-
end
-
-
1
def threads
-
28
@threads ||= []
-
end
-
-
1
def active_threads
-
8600
@active_threads ||= []
-
end
-
-
1
def threads_waiting_to_start?
-
2023
@parallel_blocks_waiting_to_start
-
end
-
-
1
def execute
-
4
active_threads.first.start
-
4
until active_threads.empty?
-
# Advance all threads to their next cycle point in sequential order. Keeping tight control of
-
# when threads are running in this way ensures that the output is deterministic no matter what
-
# computer it is running on, and ensures that the application code does not have to worry about
-
# race conditions.
-
2860
cycs = active_threads.map do |t|
-
5587
t.advance
-
5587
t.pending_cycles
-
end.compact.min
-
-
2860
if cycs
-
# Now generate the required number of cycles which is defined by the thread that has the least
-
# amount of cycles ready to go.
-
# Since tester.cycle is being called by the master process here it will generate as normal (as
-
# opposed to when called from a thread in which case it causes the thread to wait).
-
2848
cycs.cycles
-
-
# Now let each thread know how many cycles we just generated, so they can decide whether they
-
# need to wait for more cycles or if they can start preparing the next one
-
8418
active_threads.each { |t| t.executed_cycles(cycs) }
-
end
-
-
2860
if @parallel_blocks_waiting_to_start
-
4
@parallel_blocks_waiting_to_start.each do |id, block|
-
8
thread = PatternThread.new(id, self, block)
-
8
threads << thread
-
8
active_threads << thread
-
8
thread.start
-
end
-
4
@parallel_blocks_waiting_to_start = nil
-
end
-
end
-
4
@cycle_count_stop = threads.first.current_cycle_count
-
end
-
end
-
end
-
end
-
2
require 'concurrent'
-
2
module Origen
-
2
class Generator
-
# Provides APIs to enable applications to support concurrency
-
2
class PatternSequencer
-
2
class << self
-
2
def serialize(id = nil)
-
30
if active?
-
30
s = nil
-
30
id ||= caller[0]
-
30
@semaphores ||= {}
-
30
@semaphores[id] ||= Concurrent::Semaphore.new(1)
-
30
s = @semaphores[id]
-
30
completed = false
-
30
blocked = false
-
30
until completed
-
# If already acquired or available
-
751
if (thread.reservations[id] && thread.reservations[id][:semaphore]) || s.try_acquire
-
30
thread.record_active if blocked
-
30
yield
-
30
completed = true
-
else
-
721
thread.waiting_for_serialize(id, blocked)
-
721
blocked = true
-
end
-
end
-
# If the thread has reserved access to this serialized resource then don't release it now, but
-
# store a reference to the semaphore and it will be released at the end of the reserve block
-
30
if thread.reservations[id]
-
15
thread.reservations[id][:semaphore] = s
-
else
-
15
s.release
-
end
-
else
-
yield
-
end
-
end
-
-
# Once a lock is acquired on a serialize block with the given ID, it won't be released to
-
# other parallel threads until the end of this block
-
2
def reserve(id)
-
10
if active?
-
10
if thread.reservations[id]
-
5
thread.reservations[id][:count] += 1
-
else
-
5
thread.reservations[id] = { count: 1, semaphore: nil }
-
end
-
10
yield
-
10
if thread.reservations[id][:count] == 1
-
# May not be set if the application reserved the resource but never hit it
-
5
if s = thread.reservations[id][:semaphore]
-
5
s.release
-
end
-
5
thread.reservations[id] = nil
-
else
-
5
thread.reservations[id][:count] -= 1
-
end
-
else
-
yield
-
end
-
end
-
-
# Returns true if a pattern sequence is currently open/active
-
2
def active?
-
5466
!!@active
-
end
-
2
alias_method :open?, :active?
-
2
alias_method :runnng?, :active?
-
-
# Returns the PatternThread object for the current thread
-
2
def thread
-
16007
@thread.value
-
end
-
-
# Prepends the given string with "[<current thread ID>] " unless it already contains it
-
2
def add_thread(str)
-
5426
if active? && thread
-
135
id = "[#{thread.id}] "
-
135
str.prepend(id) unless str =~ /#{id}/
-
end
-
5426
str
-
end
-
-
2
private
-
-
2
def active=(val)
-
8
@active = val
-
end
-
-
2
def thread=(t)
-
14
@thread ||= Concurrent::ThreadLocalVar.new(nil)
-
14
@thread.value = t
-
end
-
end
-
end
-
end
-
end
-
2
PatSeq = Origen::Generator::PatternSequencer
-
2
PatSeq.send(:thread=, nil)
-
1
module Origen
-
1
class Generator
-
# An instance of PatternThread is created for each parallel thread of execution
-
# in a pattern sequence. One instance of this class is also created to represent
-
# the original main thread in addition to those created by calling seq.in_parallel
-
1
class PatternThread
-
# Returns the parent pattern sequence object
-
1
attr_reader :sequence
-
1
attr_reader :pending_cycles
-
1
attr_reader :id
-
1
attr_reader :reservations
-
1
attr_reader :cycle_count_start
-
1
attr_reader :cycle_count_stop
-
# A record of when the thread is active to construct the execution profile
-
1
attr_reader :events
-
-
1
def initialize(id, sequence, block, primary = false)
-
12
if primary
-
4
@cycle_count_start = 0
-
else
-
8
@cycle_count_start = current_cycle_count
-
end
-
12
@events = [[:active, cycle_count_start]]
-
12
@id = id.to_sym
-
12
@sequence = sequence
-
12
@block = block
-
12
@primary = primary
-
12
@running = Concurrent::Event.new
-
12
@waiting = Concurrent::Event.new
-
12
@pending_cycles = nil
-
12
@completed = false
-
12
@reservations = {}
-
end
-
-
# Returns true if this is main thread (the one from which all in_parallel threads
-
# have been branched from)
-
1
def primary?
-
@primary
-
end
-
-
# @api private
-
#
-
# This method is called once by the pattern sequence to start a new thread. It will block until
-
# the thread is in the waiting state.
-
1
def start
-
12
@thread = Thread.new do
-
12
PatSeq.send(:thread=, self)
-
12
wait
-
12
@block.call(sequence)
-
12
sequence.send(:thread_completed, self)
-
12
record_cycle_count_stop
-
12
@completed = true
-
12
wait
-
end
-
12
@waiting.wait
-
end
-
-
1
def record_cycle_count_stop
-
12
@cycle_count_stop = current_cycle_count
-
12
events << [:stopped, cycle_count_stop]
-
12
events.freeze
-
end
-
-
1
def record_active
-
4
events << [:active, current_cycle_count]
-
end
-
-
1
def current_cycle_count
-
32
tester.try(:cycle_count) || 0
-
end
-
-
1
def execution_profile(start, stop, step)
-
12
events = @events.dup
-
12
cycles = start
-
12
state = :inactive
-
12
line = ''
-
12
((stop - start) / step).times do |i|
-
960
active_cycles = 0
-
960
while events.first && events.first[1] >= cycles && events.first[1] < cycles + step
-
28
event = events.shift
-
# Bring the current cycles up to this event point applying the current state
-
28
if state == :active
-
12
active_cycles += event[1] - cycles
-
end
-
28
state = event[0] == :active ? :active : :inactive
-
28
cycles = event[1]
-
end
-
-
# Bring the current cycles up to the end of this profile tick
-
960
if state == :active
-
431
active_cycles += ((i + 1) * step) - cycles
-
end
-
960
cycles = ((i + 1) * step)
-
-
960
if active_cycles == 0
-
519
line += '_'
-
441
elsif active_cycles > (step * 0.5)
-
433
line += '█'
-
else
-
8
line += '▄'
-
end
-
end
-
12
line
-
end
-
-
# Will be called when the thread can't execute its next cycle because it is waiting to obtain a
-
# lock on a serialized block
-
1
def waiting_for_serialize(serialize_id, skip_event = false)
-
# puts "Thread #{id} is blocked waiting for #{serialize_id}"
-
721
events << [:waiting, current_cycle_count] unless skip_event
-
721
wait
-
end
-
-
# Will be called when the thread can't execute its next cycle because it is waiting for another
-
# thread to complete
-
1
def waiting_for_thread(skip_event = false)
-
19
events << [:waiting, current_cycle_count] unless skip_event
-
19
wait
-
end
-
-
# Will be called when the thread is ready for the next cycle
-
1
def cycle(options)
-
4835
@pending_cycles = options[:repeat] || 1
-
# If there are threads pending start and we are about to enter a long delay, block for only
-
# one cycle to give them a change to get underway and make use of this delay
-
4835
if @pending_cycles > 1 && sequence.send(:threads_waiting_to_start?)
-
1
remainder = @pending_cycles - 1
-
1
@pending_cycles = 1
-
end
-
4835
wait
-
4835
@pending_cycles = remainder if remainder
-
# If the sequence did not do enough cycles in that round to satisfy this thread, then go back
-
# around to complete the remainder before continuing with the rest of the pattern
-
4835
if @pending_cycles == 0
-
2849
@pending_cycles = nil
-
1986
elsif @pending_cycles > 0
-
1986
@pending_cycles.cycles
-
else
-
fail "Something has gone wrong @pending_cycles is #{@pending_cycles}"
-
end
-
end
-
-
# @api private
-
1
def executed_cycles(cycles)
-
5570
@pending_cycles -= cycles if @pending_cycles
-
end
-
-
1
def completed?
-
@completed
-
end
-
-
# Returns true if the thread is currently waiting for the pattern sequence to advance it
-
1
def waiting?
-
@waiting.set?
-
end
-
-
# This should be called only by the pattern thread itself, and will block it until it is told to
-
# advance by the pattern sequence running in the main thread
-
1
def wait
-
5599
@running.reset
-
5599
@waiting.set
-
5599
@running.wait
-
end
-
-
# This should be called only by the pattern sequence running in the main thread, it will un-block the
-
# pattern thread which is currently waiting, and it will block the main thread until the pattern thread
-
# reaches the next wait point (or completes)
-
1
def advance(completed_cycles = nil)
-
5587
@waiting.reset
-
5587
@running.set # Release the pattern thread
-
5587
@waiting.wait # And wait for it to reach the next wait point
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
class Generator
-
# Handles the recursive rendering and importing of sub templates
-
# and source files
-
2
module Renderer
-
2
def render(file, options = {}, &block)
-
23
fail 'File argument is nil' unless file
-
23
file = Origen.file_handler.clean_path_to_sub_template(file)
-
23
current_pipeline << { file: file, options: options,
-
placeholder: placeholder, block: block,
-
indent: options[:indent] || 0
-
}
-
23
if block_given?
-
5
self.current_buffer += current_pipeline.last[:placeholder] + "\n"
-
end
-
23
current_pipeline.last[:placeholder]
-
end
-
2
alias_method :import, :render
-
-
2
def placeholder
-
23
@ix ||= 0
-
23
@ix += 1
-
23
"_origen_render_placeholder_#{@ix}"
-
end
-
-
2
def options
-
472
@current_options ||= {}
-
end
-
-
2
def pipeline
-
372
@pipeline ||= []
-
372
@pipeline << [] if @pipeline.empty?
-
372
@pipeline
-
end
-
-
2
def current_pipeline
-
223
pipeline.last
-
end
-
-
# Insert rendered content into any placeholders
-
2
def insert(content)
-
126
while current_pipeline.size > 0
-
23
current = current_pipeline.pop
-
23
pipeline << []
-
23
@current_options = current[:options]
-
23
self.current_buffer = ''
-
23
output = compile(current[:file],
-
sub_template: true,
-
block: current[:block],
-
scope: @scope
-
)
-
23
if current[:indent] && current[:indent] > 0
-
1
indent = ' ' * current[:indent]
-
9
output = output.split("\n").map { |l| indent + l }.join("\n")
-
end
-
23
@current_options = nil
-
23
content = insert_content(content, current[:placeholder], output)
-
end
-
126
pipeline.pop
-
# Always give back a string, this is what existing callers expect
-
#
-
# Possible this could in future run into problems if the whole file cannot be read
-
# into memory, but we can cross that path when we come to it
-
126
if content.is_a?(Pathname)
-
c = content.read
-
content.delete
-
c
-
else
-
126
content
-
end
-
end
-
-
2
def insert_content(current, placeholder, content)
-
# Start using the disk for storing the output rather than memory
-
# once it starts to exceed this length
-
23
max_length = 1_000_000
-
23
if current.is_a?(Pathname) || content.is_a?(Pathname) ||
-
23
((current.length + content.length) > max_length)
-
unless current.is_a?(Pathname)
-
t = temporary_file
-
t.open('w') { |f| f.puts current }
-
current = t
-
end
-
new = temporary_file
-
new.open('w') do |new_f|
-
current.each_line do |line|
-
if line.strip == placeholder
-
if content.is_a?(Pathname)
-
content.each_line do |line|
-
new_f.puts line
-
end
-
content.delete
-
else
-
new_f.puts content.chomp
-
end
-
else
-
new_f.puts line
-
end
-
end
-
end
-
current.delete
-
new
-
else
-
23
current.sub(/ *#{placeholder}/, content)
-
end
-
end
-
-
# Returns a Pathname to a uniquely named temporary file
-
2
def temporary_file
-
# Ensure this is unique so that is doesn't clash with parallel compile processes
-
Pathname.new "#{Origen.root}/tmp/compiler_#{Process.pid}_#{Time.now.to_f}"
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
class Generator
-
# The stage provides a way to store objects in named banks for later retrieval.
-
# This is typically used during pattern generation to generate header, body and
-
# footer elements of a pattern in non-sequential order, allowing them to be
-
# combined at the end into the logical order.
-
2
class Stage
-
2
def initialize
-
2
@vault = {}
-
end
-
-
2
def reset!
-
93
@vault = {}
-
end
-
-
# Returns vectors from the end of the bank
-
2
def last_vector(offset = 0)
-
215
offset = offset.abs
-
215
i = current_bank.size - 1
-
215
while offset >= 0
-
435
return nil if i < 0
-
423
unless current_bank[i].is_a?(String)
-
247
return current_bank[i] if offset == 0
-
44
offset -= 1
-
end
-
220
i -= 1
-
end
-
end
-
-
# Same as last_vector except it returns the last objects of any
-
# type, not just vectors
-
2
def last_object(offset = 0)
-
4
i = current_bank.size - 1 - offset
-
4
current_bank[i]
-
end
-
-
# Store a new value in the current bank
-
2
def store(obj)
-
10505
current_bank.push(obj)
-
end
-
-
# Insert a new object into the current bank X places from the end
-
2
def insert_from_end(obj, x)
-
# Ruby insert is a bit un-intuative in that insert(1) will insert something 1 place in from the
-
# start, whereas insert(-1) will insert it at the end (0 places in from the end).
-
# So the subtraction of 1 here aligns the behavior when inserting from the start or the end.
-
2
current_bank.insert((x * -1) - 1, obj)
-
end
-
-
# Insert a new object into the current bank X places from the start
-
2
def insert_from_start(obj, x)
-
current_bank.insert(x, obj)
-
end
-
-
# Pull the last item added to the current bank
-
2
def newest
-
current_bank.pop
-
end
-
-
# Pull the oldest item added to the current bank
-
2
def oldest
-
current_bank.shift
-
end
-
-
# Set the current bank
-
2
def bank=(name)
-
93
@bank = name
-
end
-
-
# Returns the entire bank, an array
-
2
def bank(name = @bank)
-
258
@vault[name] || []
-
end
-
-
2
def current_bank
-
11360
return @vault[@bank] if @vault[@bank]
-
278
@vault[@bank] = []
-
end
-
-
# Temporarily switches to the given bank
-
2
def with_bank(bank)
-
279
orig_bank = @bank
-
279
@bank = bank
-
279
yield
-
279
@bank = orig_bank
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
module Location
-
1
autoload :Map, 'origen/location/map'
-
1
autoload :Base, 'origen/location/base'
-
end
-
end
-
1
module Origen
-
1
module Location
-
# A Location is an abstract object used to represent any NVM location
-
# of interest, such as a pass code, security field, etc.
-
1
class Base
-
1
attr_accessor :address, :endian, :size_in_bytes, :owner
-
-
1
alias_method :byte_address, :address
-
1
alias_method :byte_aligned_byte_address, :address
-
1
alias_method :endianess, :endian
-
-
1
def initialize(options = {})
-
options = {
-
19
size_in_bytes: 1,
-
word_size_in_bytes: 2,
-
endian: :big,
-
data: 0,
-
nil_state: 0
-
}.merge(options)
-
19
@address = options.delete(:address) || options.delete(:byte_address)
-
19
@endian = options.delete(:endian)
-
19
@size_in_bytes = options.delete(:size_in_bytes)
-
19
@nil_state = options.delete(:nil_state)
-
19
@owner = options.delete(:owner)
-
19
write(options.delete(:data), size_in_bytes: @size_in_bytes)
-
19
create_address_methods(options)
-
end
-
-
1
def aligned_address(bytes)
-
6
f = bytes - 1
-
6
(address >> f) << f
-
end
-
-
1
def big_endian?
-
43
endian == :big
-
end
-
-
1
def little_endian?
-
endian == :little
-
end
-
-
1
def write(data, options = {})
-
27
@current_data = data
-
27
@current_data_size_in_bytes = options[:size_in_bytes] || size_in_bytes
-
27
self.data(options)
-
end
-
1
alias_method :set, :write
-
-
1
def data(options = {})
-
42
data = @current_data
-
42
nil_val = options[:nil_state] || @nil_state
-
42
shift = 8 * (size_in_bytes - @current_data_size_in_bytes)
-
42
mask = (1 << shift) - 1
-
42
if big_endian?
-
32
data <<= shift
-
32
if nil_val == 1 && shift != 0
-
2
data |= mask
-
end
-
else
-
10
if nil_val == 1
-
5
data |= (mask << shift)
-
end
-
end
-
42
data
-
end
-
1
alias_method :value, :data
-
1
alias_method :val, :data
-
-
1
def read!(*args)
-
action!(:read, *args)
-
end
-
-
1
def write!
-
action!(:write, *args)
-
end
-
-
1
def store!
-
action!(:store, *args)
-
end
-
-
1
def program!
-
action!(:program, *args)
-
end
-
-
1
def erase!
-
action!(:erase, *args)
-
end
-
-
1
private
-
-
1
def action!(type, *args)
-
if owner
-
owner.send(type, self, *args)
-
else
-
fail "To #{type} a location an owner must be assigned to it!"
-
end
-
end
-
-
1
def create_address_methods(options)
-
19
options.each do |key, value|
-
21
if key.to_s =~ /(\w+)_size_in_bytes$/
-
21
define_singleton_method("#{Regexp.last_match[1].downcase}_aligned_address") do
-
3
aligned_address(value)
-
end
-
21
define_singleton_method("#{Regexp.last_match[1].downcase}_aligned_byte_address") do
-
3
aligned_address(value)
-
end
-
21
define_singleton_method("#{Regexp.last_match[1].downcase}_address") do
-
3
address / value
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
module Origen
-
1
module Location
-
1
module Map
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
1
def define_locations(defaults = {})
-
4
@x = @x ? (@x + 1) : 0 # Provides a unique ID for each define_locations block
-
4
default_attributes[@x] = defaults
-
4
@defining = true
-
4
yield
-
4
@defining = false
-
end
-
-
1
def constructor(&block)
-
2
if defining?
-
1
constructors[@x] = block
-
else
-
1
constructors[:default] = block
-
end
-
end
-
-
1
def default_constructor(attributes, defaults)
-
6
Origen::Location::Base.new(defaults.merge(attributes))
-
end
-
-
1
def definitions
-
18
@definitions ||= {}
-
end
-
-
1
def constructors
-
12
@constructors ||= {}
-
end
-
-
1
def default_attributes
-
10
@default_attributes ||= {}
-
end
-
-
1
def defining?
-
8
@defining
-
end
-
-
# A hash of constructed location objects, i.e. an entry will be cached here the first time a location
-
# is referenced outside of its initial definition, after that it will be served directly from here.
-
1
def constructed
-
16
@constructed ||= {}
-
end
-
-
# Provides accessors for all named locations, for example:
-
#
-
# $dut.nvm.fmu.ifr_map.probe1_pass
-
1
def method_missing(method, *args, &block)
-
6
if defining?
-
6
if definitions[method]
-
warning "Redefinition of map location: #{method}"
-
end
-
6
definitions[method] = { attributes: args.first, x: @x }
-
else
-
super
-
end
-
end
-
end
-
-
1
def method_missing(method, *args, &block)
-
10
klass = self.class
-
10
klass.constructed[method] || begin
-
6
definition = klass.definitions[method]
-
6
if definition
-
6
defaults = klass.default_attributes[definition[:x]] || {}
-
6
constructor = klass.constructors[definition[:x]] || klass.constructors[:default]
-
6
if constructor
-
3
instance = constructor.call(definition[:attributes], defaults)
-
else
-
3
instance = klass.default_constructor(definition[:attributes], defaults)
-
end
-
6
klass.constructed[method] = instance
-
end
-
end || super
-
end
-
end
-
end
-
end
-
2
module Origen
-
# A class to handle the Origen execution mode
-
2
class Mode
-
2
MODES = [:production, :debug, :simulation]
-
-
2
def initialize(_options = {})
-
3
@current_mode = :production
-
end
-
-
# When called any future changes to the mode will be ignored
-
2
def freeze
-
1
@frozen = true
-
end
-
-
2
def unfreeze
-
1
@frozen = false
-
end
-
-
2
def set(val)
-
723
@current_mode = find_mode(val) unless @frozen
-
end
-
-
2
def to_s
-
95
@current_mode ? @current_mode.to_s : ''
-
end
-
-
2
def find_mode(name)
-
722
name = name.to_s.downcase.to_sym
-
722
if MODES.include?(name)
-
717
name
-
else
-
5
mode = MODES.find do |m|
-
12
m.to_s =~ /^#{name}/
-
end
-
5
if mode
-
3
mode
-
else
-
2
fail "Invalid mode requested, must be one of: #{MODES}"
-
end
-
end
-
end
-
-
# Any mode which is not production will return true here, if
-
# you want to test for only debug mode use Origen.mode == :debug
-
2
def debug?
-
6
!production?
-
end
-
-
2
def production?
-
563
@current_mode == :production
-
end
-
-
2
def simulation?
-
367
@current_mode == :simulation
-
end
-
-
2
def ==(val)
-
12
if val.is_a?(Symbol)
-
12
@current_mode == val
-
else
-
super
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
module Models
-
1
autoload :ScanRegister, 'origen/models/scan_register'
-
1
autoload :Mux, 'origen/models/mux'
-
end
-
end
-
1
module Origen
-
1
module Models
-
1
class Mux
-
1
include Origen::Model
-
-
1
attr_reader :size
-
1
attr_reader :select_lines
-
-
1
def initialize(options = {})
-
2
@input = []
-
2
(2**select_lines).times do |i|
-
8
@input << port("input#{i}".to_sym, size: size)
-
end
-
-
2
port :select, size: select_lines
-
2
port :output, size: size
-
-
2
output.connect_to do |i|
-
40
unless ports[:select].data.undefined?
-
32
send("input#{ports[:select].data}")[i].path
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
module Models
-
1
class ScanRegister
-
1
include Origen::Model
-
-
1
attr_reader :size
-
-
1
def initialize(options = {})
-
# The shift register
-
6
reg :sr, 0, size: size, reset: options[:reset] || 0
-
# The update register, this is the value presented to the outside world
-
6
reg :ur, 0, size: size, reset: options[:reset] || 0
-
-
6
port :si # Scan in
-
6
port :so # Scan out
-
6
port :c, size: size # Capture in
-
-
# Control signals
-
6
port :se # Shift enable
-
6
port :ce # Capture enable
-
6
port :ue # Update enable
-
-
6
so.connect_to(sr[0])
-
end
-
-
1
def method_missing(method, *args, &block)
-
266
if BitCollection.instance_methods.include?(method)
-
4
ur.send(method, *args, &block)
-
else
-
262
super
-
end
-
end
-
-
1
def respond_to?(*args)
-
259
super(*args) || BitCollection.instance_methods.include?(args.first)
-
end
-
-
1
def mode
-
32
if se.data == 1
-
25
:shift
-
7
elsif ce.data == 1
-
1
:capture
-
6
elsif ue.data == 1
-
1
:update
-
else
-
5
undefined
-
end
-
end
-
-
1
def clock_prepare
-
32
@mode = mode
-
32
if @mode == :shift
-
25
@din = si.data
-
7
elsif @mode == :capture
-
1
@din = c.data
-
6
elsif @mode == :update
-
1
@din = sr.data
-
end
-
end
-
-
1
def clock_apply
-
32
if @mode == :shift
-
25
sr.shift_right(@din)
-
7
elsif @mode == :capture
-
1
sr.write(@din)
-
6
elsif @mode == :update
-
1
ur.write(@din)
-
end
-
32
@din = nil
-
32
@mode = nil
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Netlist
-
2
module Connectable
-
2
extend ActiveSupport::Concern
-
-
2
included do
-
6
include Origen::Netlist
-
end
-
-
2
def connect_to(node = nil, options = {}, &block)
-
33
node, options = nil, node if node.is_a?(Hash)
-
33
node = node.path if node.respond_to?(:path)
-
33
netlist.connect(path, node, &block)
-
end
-
2
alias_method :connect, :connect_to
-
end
-
end
-
end
-
2
module Origen
-
2
module Netlist
-
2
class List
-
2
attr_reader :top_level, :table
-
-
2
alias_method :parent, :top_level
-
2
alias_method :owner, :top_level
-
-
2
def initialize(top_level)
-
14
@top_level = top_level
-
14
@table = {}
-
end
-
-
# Connect two paths together in the netlist, one can be a numeric
-
# value to represent a logic level connection
-
2
def connect(a, b = nil, &block)
-
33
b ||= block
-
33
align(a, b) do |path, index, target|
-
71
table[path] ||= {}
-
71
table[path][index] ||= []
-
71
table[path][index] << target
-
end
-
end
-
-
2
def data_bit(path, index, options = {})
-
385
bits = data_bits(path, index, options)
-
385
if bits.size > 1
-
fail "Multiple data bit connections found for node #{path}[#{index}]"
-
385
elsif bits.size == 0
-
38
return undefined
-
end
-
347
bits.first
-
end
-
-
2
def data_bits(path, index, options = {})
-
846
processed_paths = options[:processed_paths] || []
-
846
bits = []
-
846
['*', index].each do |i|
-
1692
unless processed_paths.include?("#{path}[#{i}]")
-
1004
processed_paths << "#{path}[#{i}]"
-
1004
vals = (table[path] || {})[i] || []
-
# Also consider anything attached directly to the requested path, e.g. a
-
# drive value applied to a port
-
1004
vals << "#{path}[#{i}]" if i != '*' && !options[:sublevel]
-
1004
vals.each do |val|
-
816
if val.is_a?(Proc)
-
72
from_proc = true
-
72
val = val.call(index)
-
else
-
744
from_proc = false
-
end
-
816
if val.is_a?(Integer)
-
57
bits << Registers::Bit.new(nil, index, access: :ro, data: (i == '*' && !from_proc) ? val[index] : val)
-
759
elsif val
-
751
vp, vi = *to_v(val)
-
751
bc = eval("top_level.#{vp}[#{vi || index}]")
-
751
if bc.is_a?(Registers::BitCollection)
-
52
bits << bc.bit
-
699
elsif bc.is_a?(Ports::Section) && bc.drive_value
-
238
bits << Registers::Bit.new(nil, index, access: :ro, data: bc.drive_value)
-
else
-
461
bits += data_bits(vp, vi || index, processed_paths: processed_paths, sublevel: true) || []
-
end
-
end
-
end
-
end
-
end
-
846
bits.uniq
-
end
-
-
2
private
-
-
2
def align(a, b)
-
33
a, b = clean(a), clean(b)
-
33
if a[:size] || b[:size]
-
8
if a[:size] && b[:size]
-
2
size = [a[:size], b[:size]].min
-
else
-
6
size = a[:size] || b[:size]
-
end
-
-
8
unless a[:numeric] || a[:proc]
-
8
size.times do |i|
-
14
index = a[:indexes] ? a[:indexes][i] : i
-
14
if b[:numeric]
-
target = b[:path][i]
-
14
elsif b[:proc]
-
target = b[:path]
-
else
-
14
if b[:indexes]
-
14
target = "#{b[:path]}[#{b[:indexes][i]}]"
-
else
-
target = "#{b[:path]}[#{i}]"
-
end
-
end
-
14
yield a[:path], index, target
-
end
-
end
-
-
8
unless b[:numeric] || b[:proc]
-
8
size.times do |i|
-
14
index = b[:indexes] ? b[:indexes][i] : i
-
14
if a[:numeric]
-
target = a[:path][i]
-
14
elsif a[:proc]
-
target = a[:path]
-
else
-
14
if a[:indexes]
-
8
target = "#{a[:path]}[#{a[:indexes][i]}]"
-
else
-
6
target = "#{a[:path]}[#{i}]"
-
end
-
end
-
14
yield b[:path], index, target
-
end
-
end
-
-
else
-
25
yield a[:path], '*', b[:path] unless a[:numeric] || a[:proc]
-
25
yield b[:path], '*', a[:path] unless b[:numeric] || b[:proc]
-
end
-
end
-
-
2
def clean(path)
-
66
if path.is_a?(Integer)
-
3
{ path: path, numeric: true }
-
63
elsif path.is_a?(Proc)
-
4
{ path: path, proc: true }
-
else
-
59
if path =~ /(.*)\[(\d+):?(\d*)\]$/
-
10
if Regexp.last_match(3).empty?
-
6
{ path: Regexp.last_match(1), size: 1, indexes: [Regexp.last_match(2).to_i] }
-
else
-
4
a = ((Regexp.last_match(2).to_i)..(Regexp.last_match(3).to_i)).to_a
-
4
{ path: Regexp.last_match(1), size: a.size, indexes: a }
-
end
-
else
-
49
{ path: path }
-
end
-
end
-
end
-
-
2
def to_v(path)
-
751
if path =~ /(.*)\[(\d+)\]$/
-
387
[Regexp.last_match(1), Regexp.last_match(2).to_i]
-
else
-
364
[path, nil]
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
class OrgFile
-
2
autoload :Interceptor, 'origen/org_file/interceptor'
-
2
autoload :Interceptable, 'origen/org_file/interceptable'
-
-
2
class << self
-
2
def new(*args, &block)
-
if @internal_new
-
super
-
else
-
open(*args, &block)
-
end
-
end
-
-
2
def open(id, options = {})
-
fail "An org_file is already open with id: #{id}" if open_files[id]
-
@internal_new = true
-
f = OrgFile.new(id, options)
-
@internal_new = nil
-
open_files[id] = f
-
if block_given?
-
yield f
-
close(id, options)
-
else
-
f
-
end
-
end
-
-
2
def close(id, options = {})
-
fail "An org_file with this ID has not been opened id: #{id}" unless open_files[id]
-
open_files[id].close unless options[:_internal_org_file_call_]
-
open_files.delete(id)
-
end
-
-
2
def org_file(id = nil)
-
id ? open_files[id] : open_files.to_a.last[1]
-
end
-
-
2
def cycle(number = 1)
-
open_files.each { |id, f| f.cycle(number) }
-
end
-
-
2
private
-
-
2
def open_files
-
@open_files ||= {}.with_indifferent_access
-
end
-
end
-
-
2
attr_accessor :path, :filename
-
2
attr_reader :id, :operation
-
-
2
def initialize(id, options = {})
-
@id = id
-
@path = options[:path] || default_filepath
-
@filename = options[:filename] || "#{id}.org"
-
FileUtils.mkdir_p(path)
-
@path_to_file = File.join(path, filename)
-
end
-
-
2
def default_filepath
-
"#{Origen.root}/pattern/org/#{Origen.target.name}"
-
end
-
-
2
def file
-
@file ||= begin
-
if operation == 'r'
-
fail "No org file found at: #{@path_to_file}" unless exist?
-
end
-
File.open(@path_to_file, operation)
-
end
-
end
-
-
2
def exist?
-
File.exist?(@path_to_file)
-
end
-
-
2
def close
-
# Corner case, if no call to read_line or record was made then no file was created and there
-
# is no file to close
-
if @file
-
file.puts "#{@buffer}#{@buffer_cycles}" if @buffer
-
file.close
-
end
-
self.class.close(id, _internal_org_file_call_: true)
-
nil
-
end
-
-
2
def read_line
-
@operation ||= 'r'
-
operations = file.readline.strip.split(';')
-
cycles = operations.pop.to_i
-
operations = operations.map { |op| op.split(',') }.map { |op| op[0] = eval(op[0]); op }
-
if block_given?
-
yield operations, cycles
-
else
-
[operations, cycles]
-
end
-
end
-
-
2
def record(path_to_object, method, *args)
-
@operation ||= 'w'
-
@line ||= ''
-
@line += "#{path_to_object},#{method}"
-
@line += ",#{args.join(',')}" unless args.empty?
-
@line += ';'
-
end
-
-
2
def cycle(number)
-
if @buffer
-
if @line == @buffer
-
@buffer_cycles += number
-
else
-
file.puts "#{@buffer}#{@buffer_cycles}"
-
@buffer = @line
-
@buffer_cycles = number
-
end
-
else
-
@buffer = @line
-
@buffer_cycles = number
-
end
-
@line = nil
-
end
-
end
-
end
-
2
module Origen
-
2
class OrgFile
-
2
module Interceptable
-
2
def self.included(base)
-
4
base.extend ClassMethods
-
end
-
-
2
module ClassMethods
-
2
def new(*args, &block)
-
8560
o = allocate
-
8560
i = OrgFile::Interceptor.new(o)
-
8560
o.__interceptor__ = i
-
8560
i.send(:initialize, *args, &block)
-
8560
unless o.respond_to?(:global_path_to)
-
puts 'When adding the OrgFile::Interceptable module to a class, the class must define an instance method called "global_path_to", like this:'
-
puts
-
puts ' # Must return a string that contains a global path to access the given object,'
-
puts ' # here for example if the object was a pin'
-
puts ' def global_path_to'
-
puts ' "dut.pins(:#{id})"'
-
puts ' end'
-
fail "Incomplete integration of OrgFile::Interceptable in #{o.class}"
-
end
-
8560
i
-
end
-
end
-
-
# Class which include OrgFile::Interceptor should use 'myself' anytime then want to reference 'self',
-
# this ensures that there are never any references to the unwrapped object
-
2
def myself
-
26370
@__interceptor__
-
end
-
-
# @api private
-
2
def __interceptor__=(obj)
-
8560
@__interceptor__ = obj
-
end
-
end
-
end
-
end
-
2
require 'set'
-
2
module Origen
-
2
class OrgFile
-
# @api private
-
#
-
# Helper for the Interceptor where block_given? doesn't work internally
-
2
def self._block_given_?(&block)
-
block_given?
-
end
-
-
2
class Interceptor < ::BasicObject
-
2
def initialize(object, options = {})
-
8560
@object = object
-
8560
@@locked = false unless defined? @@locked
-
end
-
-
2
def inspect(*args)
-
@object.inspect(*args)
-
end
-
-
2
def ==(obj)
-
87863
if obj.respond_to?(:__org_file_interceptor__)
-
87789
@object == obj.__object__
-
else
-
74
@object == obj
-
end
-
end
-
2
alias_method :equal?, :==
-
-
2
def record_to_org_file(id = nil, options = {})
-
id, options = nil, id if id.is_a?(::Hash)
-
if options[:only]
-
@org_file_methods_to_intercept = Array(options[:only]).to_set
-
else
-
@org_file_methods_to_intercept = default_org_file_captures
-
if options[:also]
-
@org_file_methods_to_intercept += Array(options[:also]).to_set
-
end
-
end
-
@org_file ||= @old_org_file || OrgFile.org_file(id)
-
end
-
-
# Temporarily stop recording operations within the given block, or stop recording
-
# completely if no block given
-
2
def stop_recording_to_org_file(&block)
-
@old_org_file = @org_file
-
if OrgFile._block_given_?(&block)
-
@org_file = nil
-
yield
-
@org_file = @old_org_file
-
else
-
@org_file = nil
-
end
-
end
-
-
2
def method_missing(method, *args, &block)
-
569124
if !@@locked && @org_file && @org_file_methods_to_intercept.include?(method)
-
@org_file.record(@object.global_path_to, method, *args)
-
# Locking prevents an operation on an intercepted container object trigger from generating multiple
-
# org file entries if its contained objects are also intercepted. e.g. Imagine this is a pin group, we
-
# want the org file to reflect the operation called on the pin group, but not the many subsequent internal
-
# operations as the group proxies the operation to its contained pins
-
@@locked = true
-
@object.send(method, *args, &block)
-
@@locked = false
-
else
-
569124
@object.send(method, *args, &block)
-
end
-
end
-
-
2
def respond_to?(method, include_private = false)
-
87932
method == :__org_file_interceptor__ ||
-
@object.respond_to?(method, include_private)
-
end
-
-
2
def __org_file_interceptor__
-
true
-
end
-
-
# @api private
-
#
-
# Don't ever use this! An un-wrapped reference to an object must never make it into
-
# application code or else any operations called on the un-wrapped reference will not
-
# be captured.
-
2
def __object__
-
87789
@object
-
end
-
-
2
private
-
-
2
def debugger
-
::Kernel.debugger
-
end
-
-
2
def default_org_file_captures
-
@default_captures ||= Array(@object.try(:org_file_intercepted_methods)).to_set
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Parameters
-
2
require 'delegate'
-
2
class Live < ::Delegator
-
2
def initialize(options)
-
31
@owner = options[:owner]
-
31
@path = options[:path].split('.')
-
31
@name = options[:name]
-
end
-
-
2
def __getobj__
-
136
p = @owner.params
-
275
@path.each { |pt| p = p.send(pt) }
-
136
p.send(@name)
-
end
-
-
2
def is_a_live_parameter?
-
3
true
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Parameters
-
# An instance of this class is returned whenever the parameter context is set to
-
# a value for which no parameter set has been defined.
-
#
-
# Sometime this may be valid in the case where the context is actually implemented
-
# by another object which shadows the context.
-
#
-
# The missing allows the user to do params.context to retrieve the value of the
-
# current context, but it will error out with a useful error message if they try
-
# to do anything else (i.e. retrieve a parameter from it)
-
2
class Missing
-
2
attr_reader :owner
-
-
2
def initialize(options = {})
-
2
@owner = options[:owner]
-
end
-
-
2
def context
-
2
owner._parameter_current
-
end
-
-
2
def method_missing(_method, *_args, &_block)
-
1
owner.send(:_validate_parameter_set_name, context)
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Parameters
-
2
class Set < Hash
-
2
attr_accessor :top_level
-
2
attr_accessor :name
-
2
attr_accessor :path
-
-
# Allow these parameter names to be valid. When used, they will override the
-
# methods of the same name provided by the Hash class.
-
2
OVERRIDE_HASH_METHODS = [:min, :max]
-
-
# Allow these parameter names to be valid. When used, they will override the
-
# methods of the same name provided by another class.
-
2
OVERRIDE_METHODS = [:chain]
-
-
2
def initialize(options = {})
-
1284
if options[:top_level]
-
308
@top_level = self
-
308
@path = ''
-
308
@owner = options[:owner]
-
end
-
end
-
-
2
def define(parent = nil, &_block)
-
320
@defining = true
-
320
yield self, parent
-
320
@defining = false
-
320
finalize unless Origen::Parameters.transaction_open
-
end
-
-
# Returns the current parameter context
-
2
def context
-
5
owner._parameter_current
-
end
-
2
alias_method :current_context, :context
-
-
2
def available_contexts
-
4
owner._parameter_sets.keys
-
end
-
2
alias_method :contexts, :available_contexts
-
-
2
def copy_defaults_from(set)
-
992
set.each do |name, val|
-
2509
if val.is_a?(Set)
-
790
self[name] ||= new_subset(name)
-
790
self[name].copy_defaults_from(val)
-
else
-
1719
self[name] = val
-
end
-
end
-
end
-
-
2
def method_missing(method, *args, &block)
-
2232
if defining?
-
1647
if args.length == 0
-
675
self[method] ||= new_subset(method)
-
972
elsif args.length > 1
-
super
-
else
-
972
m = method.to_s.sub('=', '').to_sym
-
972
self[m] = args.first
-
end
-
else
-
585
if args.length != 0
-
6
super
-
else
-
579
if !key?(method)
-
nil
-
else
-
576
val = self[method]
-
576
if val.is_a?(Set)
-
235
val
-
else
-
341
if live?
-
31
Live.new(owner: owner, path: path, name: method)
-
else
-
310
if val.is_a?(Proc)
-
val.call(*args)
-
else
-
310
val
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
-
2
(OVERRIDE_METHODS + OVERRIDE_HASH_METHODS).each do |method|
-
6
define_method method do
-
27
method_missing(method)
-
end
-
end
-
-
2
def each
-
2273
super do |key, val|
-
5728
if val.is_a?(Proc)
-
228
yield key, val.call
-
else
-
5500
yield key, val
-
end
-
end
-
end
-
-
2
def [](key)
-
2832
val = super
-
2832
val.is_a?(Proc) ? val.call : val
-
end
-
-
# Test seems to be some kind of reserved word, that doesn't trigger the method_missing,
-
# so re-defining it here to allow a param group called 'test'
-
2
def test(*args, &block)
-
172
method_missing(:test, *args, &block)
-
end
-
-
2
def defining?
-
3056
return true if Origen::Parameters.transaction_open
-
2850
if top_level?
-
2026
@defining
-
else
-
824
top_level.defining?
-
end
-
end
-
-
2
def owner
-
653
if top_level?
-
413
@owner
-
else
-
240
top_level.owner
-
end
-
end
-
-
2
def top_level?
-
3503
top_level == self
-
end
-
-
2
def finalize
-
1224
freeze
-
4297
each { |_name, val| val.finalize if val.is_a? Set }
-
end
-
-
2
def new_subset(name)
-
976
set = Set.new
-
976
set.name = name
-
976
set.top_level = top_level
-
976
if path == ''
-
817
set.path = name.to_s
-
else
-
159
set.path = "#{path}.#{name}"
-
end
-
976
set
-
end
-
-
2
def live?
-
341
owner._live_parameter_requested?
-
end
-
-
2
def live
-
32
owner._request_live_parameter
-
32
self
-
end
-
-
2
def to_flat_hash(options = {})
-
options = {
-
8
delimiter: '.'
-
}.update(options)
-
8
flatten_params(self, options[:delimiter]).first
-
end
-
-
2
private
-
-
2
def flatten_params(param_hash, delimiter, name = nil, results_hash = {})
-
56
param_hash.each do |k, v|
-
144
if v.is_a? Origen::Parameters::Set
-
48
name.nil? ? name = k.to_s : name << "#{delimiter}#{k}"
-
48
(results_hash, name) = flatten_params(v, delimiter, name, results_hash)
-
else
-
96
if name.nil?
-
8
results_hash[k] = v
-
else
-
88
results_hash["#{name}#{delimiter}#{k}"] = v
-
88
if k == param_hash.keys.last
-
48
name = name.include?(delimiter) ? name.split(delimiter)[0..-2].join(delimiter) : nil
-
end
-
end
-
end
-
end
-
56
[results_hash, name]
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Pins
-
2
require 'delegate'
-
2
require 'origen/pins/pin'
-
# Thin wrapper around pin objects to implement a defined function.
-
#
-
# The pin object stores all attributes associated with the function, this
-
# wrapper simply keeps track of what function a given pin reference refers to
-
2
class FunctionProxy < ::Delegator
-
2
def initialize(id, pin)
-
22
@id = id
-
22
@pin = pin
-
end
-
-
2
def __getobj__
-
28
@pin
-
end
-
-
# @api private
-
#
-
# To play nicely with == when a function proxy is wrapping a pin that is already
-
# wrapped by an OrgFile interceptor
-
2
def __object__
-
2
@pin.__object__
-
end
-
-
# Intercept all calls to function-scoped attributes of the pin so
-
# that we can inject the function that we want the attribute value for
-
2
Pin::FUNCTION_SCOPED_ATTRIBUTES.each do |attribute|
-
12
define_method attribute do |options = {}|
-
10
options[:function] = @id
-
10
@pin.send(attribute, options)
-
end
-
end
-
-
2
private
-
-
# For debug
-
2
def _function
-
@id
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Pins
-
2
class GroundPin < Pin
-
end
-
end
-
end
-
2
module Origen
-
2
module Pins
-
2
class OtherPin < Pin
-
end
-
end
-
end
-
2
module Origen
-
2
module Pins
-
2
class Pin
-
2
include PinCommon
-
2
include OrgFile::Interceptable
-
-
# Don't include the ! method in here, the cycle will be captured at the tester level and
-
# it would cause a double cycle in the org file if also captured at the pin
-
2
ORG_FILE_INTERCEPTED_METHODS = [
-
:suspend, :resume, :repeat_previous=,
-
:drive_hi, :write_hi, :drive_very_hi, :drive_lo, :write_lo, :drive_mem, :expect_mem,
-
:assert_hi, :expect_hi, :compare_hi, :read_hi, :assert_lo, :expect_lo, :compare_lo, :read_lo, :dont_care,
-
:drive, :write, :assert, :compare, :expect, :read, :assert_midband, :compare_midband, :expect_midband, :read_midband,
-
:toggle, :capture, :store
-
]
-
-
# Any attributes listed here will be looked up for the current function defined
-
# by the current mode and configuration context before falling back to a default
-
2
FUNCTION_SCOPED_ATTRIBUTES = [:name, :direction, :option, :group, :ip_block, :meta]
-
-
# Any attributes listed here will be looked up for the current package context
-
# before falling back to a default
-
2
PACKAGE_SCOPED_ATTRIBUTES = [:location, :dib_assignment, :dib_meta]
-
-
# Pin Types
-
2
TYPES = [:analog, :digital]
-
-
2
attr_accessor :order
-
# Inverts pin states for drive and compare, can be useful if a timing set change requires clocks to drive low for example when all pattern logic has been set up to drive them high.
-
2
attr_accessor :invert
-
# Attribute used to generate vectors where the pin state is assigned the
-
# repeat_previous opcode, used by Tester#repeat_previous
-
2
attr_accessor :repeat_previous
-
2
attr_reader :owner
-
2
attr_reader :size
-
# Returns a hash containing the aliases associated with the given pin
-
2
attr_reader :aliases
-
# Returns a hash containing the functions associated with the given pin
-
2
attr_reader :functions
-
# Internal power supply pin is connected to
-
2
attr_accessor :supply
-
2
attr_accessor :supply_str
-
# Boolean on whether pin is open drain
-
2
attr_accessor :open_drain
-
# Boolean on whether pin has external pull-up
-
2
attr_accessor :ext_pullup
-
# Boolean on whether pin has external pull-down
-
2
attr_accessor :ext_pulldown
-
# Pin type, either :analog or :digital
-
2
attr_accessor :type
-
# Pin RTL name
-
2
attr_accessor :rtl_name
-
# Value to be forced on the pin, e.g. during simulation
-
2
attr_accessor :force
-
-
2
attr_accessor :description
-
2
attr_accessor :notes
-
-
# Returns a hash containing any meta data associated with the current pin state
-
#
-
# my_pin.read!(1, meta: { position: 10 })
-
# my_pin.state_meta # => { position: 10 }
-
# my_pin.dont_care
-
# my_pin.state_meta # => {}
-
2
attr_reader :state_meta
-
-
# Should be instantiated through the HasPins macros
-
2
def initialize(id, owner, options = {}) # :nodoc:
-
options = {
-
7236
reset: :dont_care,
-
invert: false,
-
direction: :io,
-
open_drain: false,
-
ext_pullup: false,
-
ext_pulldown: false,
-
rtl_name: nil
-
}.merge(options)
-
7236
@aliases = {}
-
7236
@functions = {}
-
7236
@direction = sanitize_direction(options[:direction])
-
7236
@invert = options[:invert]
-
7236
@reset = options[:reset]
-
7236
@force = options[:force] & 1
-
7236
@id = id
-
7236
@name = options[:name]
-
7236
@rtl_name = options[:rtl_name]
-
7236
@suspend = false
-
7236
@order = options[:order]
-
7236
@supply = options[:supply]
-
7236
@open_drain = options[:open_drain]
-
7236
@ext_pullup = options[:ext_pullup]
-
7236
@ext_pulldown = options[:ext_pulldown]
-
7236
@type = options[:type]
-
7236
@dib_assignment = [] # Array to handle multi-site testing
-
7236
@size = 1
-
7236
@value = 0
-
7236
@clock = nil
-
7236
@meta = options[:meta] || {}
-
7236
@dib_meta = options[:dib_meta] || {}
-
7236
@state_meta = {}
-
7236
@_saved_state = []
-
7236
@_saved_value = []
-
7236
@_saved_suspend = []
-
7236
@_saved_invert = []
-
7236
@_saved_repeat_previous = []
-
7236
on_init(owner, options)
-
# Assign the initial state from the method so that any inversion is picked up...
-
7236
send(@reset)
-
end
-
-
2
def global_path_to
-
"dut.pins(:#{id})"
-
end
-
-
2
def org_file_intercepted_methods
-
ORG_FILE_INTERCEPTED_METHODS
-
end
-
-
# Returns the drive cycle wave assigned to the pin based on the currently enabled timeset,
-
# or nil if none is set.
-
# Note that if a timeset is set then all pins will always return a wave as they will pick
-
# up a default waveform if none is explicitly assigned to it.
-
2
def drive_wave(code = nil)
-
26
if t = dut.current_timeset
-
# Cache this for performance since potentially this is something that could be called on
-
# every cycle in some applications
-
25
@drive_waves ||= {}
-
25
@drive_waves[t.id] ||= {}
-
25
@drive_waves[t.id][code] ||= dut.current_timeset.send(:wave_for, myself, type: :drive, code: code)
-
end
-
end
-
-
# Returns the compare cycle wave assigned to the pin based on the currently enabled timeset,
-
# or nil if none is set
-
# Note that if a timeset is set then all pins will always return a wave as they will pick
-
# up a default waveform if none is explicitly assigned to it.
-
2
def compare_wave(code = nil)
-
18
if t = dut.current_timeset
-
# Cache this for performance since potentially this is something that could be called on
-
# every cycle in some applications
-
18
@compare_waves ||= {}
-
18
@compare_waves[t.id] ||= {}
-
18
@compare_waves[t.id][code] ||= dut.current_timeset.send(:wave_for, myself, type: :compare, code: code)
-
end
-
end
-
-
2
def rtl_name
-
6
if primary_group
-
4
(@rtl_name || "#{primary_group.id}#{primary_group_index}").to_s
-
else
-
2
(@rtl_name || id).to_s
-
end
-
end
-
-
# Causes the pin to continuously drive 1 for 2 seconds and then drive 0 for 2 seconds.
-
#
-
# This is not an API that is intended to be used within a pattern. Rather it is a debug aid when
-
# setting up something like a bench test environment that uses Origen Link. For example you would
-
# call this method on a pin from a console session, then confirm with a multimeter that the pin
-
# is toggling on the relevant hardware.
-
#
-
# Call Pin#goodbye to stop it.
-
#
-
# @example Call from an origen console like this
-
#
-
# dut.pin(:tdi).hello
-
2
def hello
-
drive_hi
-
@@hello_pins ||= []
-
@@hello_pins << myself unless @@hello_pins.include?(myself)
-
@@hello_loop ||= Thread.new do
-
loop do
-
@@hello_pins.each(&:toggle)
-
if $tester
-
# Add a dummy timeset if one is not set yet, doesn't really matter what it is in this case
-
# and better not to force the user to setup a debug workaround due to running outside of a pattern
-
$tester.set_timeset('hello_world', 40) unless $tester.timeset
-
$tester.cycle
-
end
-
sleep 2
-
end
-
end
-
puts "Pin #{name} is toggling with a period of 2 seconds"
-
end
-
-
# See Pin#hello
-
2
def goodbye
-
@@hello_pins.delete(myself)
-
puts "Pin #{name} has stopped toggling"
-
end
-
-
# When sorting pins do it by ID
-
2
def <=>(other_pin)
-
3
@id <=> other_pin.id
-
end
-
-
2
def name=(val)
-
34
@name = val
-
end
-
-
2
def functions=(val)
-
if val.is_a? Hash
-
val.each do |name, _whatever|
-
add_function name
-
end
-
else
-
fail "Attempt to set the functions hash on pin #{@name}. Argument must be a Hash."
-
end
-
end
-
-
# This generates getter methods that will lookup the given attribute within the
-
# scope of the current package and falling back to a default if defined
-
2
PACKAGE_SCOPED_ATTRIBUTES.each do |attribute|
-
6
define_method attribute do |options = {}|
-
19
default = instance_variable_get("@#{attribute}")
-
19
package_id = options[:package] || current_package_id
-
19
package_id = package_id.to_sym if package_id
-
19
if packages[package_id]
-
18
packages[package_id][attribute] || default
-
1
elsif packages[:all]
-
packages[:all][attribute] || default
-
else
-
1
default
-
end
-
end
-
end
-
-
# This generates getter methods that will lookup the given attribute within the
-
# scope of the function enabled by the current mode and configuration attributes
-
# and falling back to a default if defined
-
2
FUNCTION_SCOPED_ATTRIBUTES.each do |attribute|
-
12
define_method attribute do |options = {}|
-
3630
default = instance_variable_get("@#{attribute}")
-
3630
if options[:function]
-
10
v = functions[:ids][options[:function]][attribute]
-
10
if v
-
10
if v.is_a?(Hash) && default.is_a?(Hash)
-
return default.merge(v) # v will overwrite any default values
-
else
-
10
return v
-
end
-
end
-
# else fall back to context-based lookup
-
end
-
3620
mode_id = options[:mode] || current_mode_id
-
3620
mode_id = mode_id.to_sym if mode_id
-
3620
mode = functions[mode_id] || functions[:all]
-
3620
if mode
-
42
config_id = options[:configuration] || options[:config] || current_configuration
-
42
config_id = config_id.to_sym if config_id
-
42
configuration = mode[config_id] || mode[:all]
-
42
if configuration
-
40
v = configuration[attribute]
-
40
if v
-
38
if v.is_a?(Hash) && default.is_a?(Hash)
-
6
return default.merge(v) # v will overwrite any default values
-
else
-
32
return v
-
end
-
else
-
2
default
-
end
-
else
-
2
default
-
end
-
else
-
3578
default
-
end
-
end
-
end
-
-
2
alias_method :function_scoped_name, :name
-
-
# Returns the name of the pin, if a name has been specifically assigned by the application
-
# (via name=) then this will be returned, otherwise the name of the current function if present
-
# will be returned, and then as a last resort the ID of the pin
-
2
def name(options = {})
-
# Return a specifically assigned name in preference to a function name
-
12619
(options.empty? ? @name : nil) || function_scoped_name(options) || @id
-
end
-
-
# Returns the value held by the pin as a string formatted to the current tester's pattern syntax
-
#
-
# @example
-
#
-
# pin.drive_hi
-
# pin.to_vector # => "1"
-
# pin.expect_lo
-
# pin.to_vector # => "L"
-
2
def to_vector
-
123294
@vector_formatted_value ||= Origen.tester.format_pin_state(myself)
-
end
-
-
# @api private
-
2
def invalidate_vector_cache
-
43503
@vector_formatted_value = nil
-
57217
groups.each { |_name, group| group.invalidate_vector_cache }
-
end
-
-
# Set the pin value and state from a string formatted to the current tester's pattern syntax,
-
# this is the opposite of the to_vector method
-
#
-
# @example
-
#
-
# pin.vector_formatted_value = "L"
-
# pin.driving? # => false
-
# pin.value # => 0
-
# pin.vector_formatted_value = "1"
-
# pin.driving? # => true
-
# pin.value # => 1
-
2
def vector_formatted_value=(val)
-
26
unless @vector_formatted_value == val
-
22
Origen.tester.update_pin_from_formatted_state(myself, val)
-
22
@vector_formatted_value = val
-
end
-
end
-
-
2
def inspect
-
"<#{myself.class}:#{object_id}>"
-
end
-
-
2
def describe(options = {})
-
1
desc = ['********************']
-
1
desc << "Pin id: #{id}"
-
1
func_aliases = []
-
1
unless functions.empty?
-
1
desc << ''
-
1
desc << 'Functions'
-
1
desc << '---------'
-
1
functions.each do |mode, configurations|
-
3
unless mode == :ids
-
2
configurations.each do |configuration, attrs|
-
2
a = ":#{attrs[:name]}".ljust(30)
-
2
func_aliases << attrs[:name]
-
2
unless mode == :all
-
4
a += ":modes => [#{[mode].flatten.map { |id| ':' + id.to_s }.join(', ')}]"
-
2
prev = true
-
end
-
2
unless configuration == :all
-
a += ' ; ' if prev
-
a += ":configurations => [#{[configuration].flatten.map { |id| ':' + id.to_s }.join(', ')}]"
-
end
-
2
desc << a
-
end
-
end
-
end
-
end
-
1
unless aliases.empty?
-
1
desc << ''
-
1
desc << 'Aliases'
-
1
desc << '-------'
-
1
aliases.each do |name, context|
-
3
unless func_aliases.include?(name)
-
1
a = ":#{name}".ljust(30)
-
1
unless context[:packages].empty? || context[:packages] == [:all]
-
a += ":packages => [#{context[:packages].map { |id| ':' + id.to_s }.join(', ')}]"
-
prev = true
-
end
-
1
unless context[:modes].empty? || context[:modes] == [:all]
-
a += ' ; ' if prev
-
a += ":modes => [#{context[:modes].map { |id| ':' + id.to_s }.join(', ')}]"
-
prev = true
-
end
-
1
unless context[:configurations].empty? || context[:configurations] == [:all]
-
a += ' ; ' if prev
-
a += ":configurations => [#{context[:configurations].map { |id| ':' + id.to_s }.join(', ')}]"
-
end
-
1
desc << a
-
end
-
end
-
end
-
1
unless Origen.top_level.modes.empty?
-
1
desc << ''
-
1
desc << 'Modes'
-
1
desc << '-------'
-
1
Origen.top_level.modes.each do |name|
-
4
unless option(mode: name).nil?
-
a = ":#{name}".ljust(30) + ":mode => #{option(mode: name)}"
-
desc << a
-
end
-
end
-
end
-
1
unless groups.empty?
-
desc << ''
-
desc << 'Groups'
-
desc << '------'
-
desc << groups.map { |name, _group| ':' + name.to_s }.join(', ')
-
end
-
1
desc << '********************'
-
1
if options[:return]
-
1
desc
-
else
-
puts desc.join("\n")
-
end
-
end
-
-
# If the pin was defined initially as part of a group then this will return that group,
-
# otherwise it will return nil
-
2
def group
-
10
@primary_group
-
end
-
2
alias_method :primary_group, :group
-
-
# If the pin is a member of a primary group, this returns its index number within that
-
# group, otherwise returns nil
-
2
def group_index
-
4
@primary_group_index
-
end
-
2
alias_method :primary_group_index, :group_index
-
-
# Returns a hash containing the pin groups that the given pin is a member of
-
2
def groups
-
# Origen.pin_bank.all_pin_groups.select do |name, group|
-
66669
@groups ||= Origen.pin_bank.pin_groups.select do |_name, group|
-
7551
group.include?(myself)
-
end
-
end
-
2
alias_method :pin_groups, :groups
-
-
2
def invalidate_group_cache
-
8476
@groups = nil
-
end
-
-
# Add a location identifier to the pin, this is a free format field which can be a
-
# pin number or BGA co-ordinate for example.
-
#
-
# @example Adding a location by package
-
# $dut.pin(:pin3).add_location "B3", :package => :p1
-
# $dut.pin(:pin3).add_location "B2", :package => :p2
-
2
def add_location(str, options = {})
-
34
packages = resolve_packages(options)
-
34
if packages.empty?
-
1
@location = str
-
1
add_alias str.to_s.symbolize, package: :all, mode: :all, configuration: :all
-
else
-
33
packages.each do |package_id|
-
33
package_id = package_id.respond_to?(:id) ? package_id.id : package_id
-
33
myself.packages[package_id] ||= {}
-
33
myself.packages[package_id][:location] = str
-
33
add_alias str.to_s.symbolize, package: package_id, mode: :all, configuration: :all
-
end
-
end
-
end
-
2
alias_method :add_locn, :add_location
-
-
# Add a Device Interface Board (e.g. probecard at wafer probe or loadboard at final package test)
-
# assignment to the pin. Some refer to this as a channel but API name is meant to be generic.
-
2
def add_dib_assignment(str, options = {})
-
options = {
-
site: 0
-
}.merge(options)
-
packages = resolve_packages(options)
-
if packages.empty?
-
@dib_assignment[options[:site]] = str
-
add_alias str.to_s.symbolize, package: :all, mode: :all, configuration: :all
-
else
-
packages.each do |package_id|
-
package_id = package_id.respond_to?(:id) ? package_id.id : package_id
-
myself.packages[package_id] ||= {}
-
myself.packages[package_id][:dib_assignment] ||= []
-
myself.packages[package_id][:dib_assignment][options[:site]] = str
-
add_alias str.to_s.symbolize, package: package_id, mode: :all, configuration: :all
-
end
-
end
-
end
-
2
alias_method :add_dib_info, :add_dib_assignment
-
2
alias_method :add_channel, :add_dib_assignment
-
-
2
def add_dib_meta(pkg, options)
-
2
unless Origen.top_level.packages.include? pkg
-
Origen.log.error("Cannot add DIB metadata for package '#{pkg}', that package has not been added yet!")
-
fail
-
end
-
2
options.each do |attr, attr_value|
-
12
packages[pkg][:dib_meta] ||= {}
-
12
packages[pkg][:dib_meta][attr] = attr_value
-
12
add_alias attr_value.to_s.symbolize, package: pkg, mode: :all, configuration: :all
-
end
-
end
-
-
# Returns the number of test sites enabled for the pin
-
2
def sites
-
1
dib_assignment.size
-
end
-
-
2
def sanitize_direction(val)
-
7272
if val
-
7272
val = val.to_s.downcase.gsub(/\//, '')
-
7272
if val =~ /i.*o/
-
7225
:io
-
47
elsif val =~ /^i/
-
25
:input
-
22
elsif val =~ /^o/
-
22
:output
-
else
-
fail "Unknown pin direction: #{val}"
-
end
-
end
-
end
-
-
# Sets the default direction of the pin, :input, :output or :io (default). If a function specific
-
# direction has been specified that will override this value.
-
2
def direction=(val)
-
21
@direction = sanitize_direction(val)
-
end
-
-
# Add a function to the pin.
-
#
-
# @example Adding a mode-specific function
-
# pin.add_function :tdi, :direction => :input
-
# pin.add_function :nvm_fail, :mode => :nvmbist, :direction => :output
-
2
def add_function(id, options = {})
-
22
id = id.to_sym
-
22
add_function_attributes(options.merge(name: id, id: id.to_sym))
-
22
f = FunctionProxy.new(id, myself)
-
22
add_alias id, packages: :all, obj: f
-
end
-
-
2
def add_function_attributes(options = {})
-
25
id = options.delete(:id)
-
25
modes = resolve_modes(options)
-
25
configurations = resolve_configurations(options)
-
25
options[:direction] = sanitize_direction(options[:direction]) if options[:direction]
-
25
if modes.empty?
-
8
modes = [:all]
-
end
-
25
if configurations.empty?
-
20
configurations = [:all]
-
end
-
# Supports newer attribute lookup by function ID
-
25
if id
-
22
functions[:ids] ||= {}
-
22
if functions[:ids][id]
-
functions[:ids][id] = functions[:ids][id].merge!(options)
-
else
-
22
functions[:ids][id] = options.dup
-
end
-
end
-
# Supports older attribute lookup by mode context
-
25
modes.each do |mode|
-
25
configurations.each do |configuration|
-
25
functions[mode.to_sym] ||= {}
-
25
if functions[mode.to_sym][configuration.to_sym]
-
2
functions[mode.to_sym][configuration.to_sym] = functions[mode.to_sym][configuration.to_sym].merge!(options)
-
else
-
23
functions[mode.to_sym][configuration.to_sym] = options
-
end
-
end
-
end
-
end
-
-
# Add an alias to the given pin.
-
#
-
# If the options contain a package, mode or configuration reference then the alias
-
# will only work under that context.
-
2
def add_alias(id, options = {})
-
1407
obj = options.delete(:obj) || myself
-
1407
if aliases[id]
-
1
aliases[id][:packages] += resolve_packages(options)
-
1
aliases[id][:modes] += resolve_modes(options)
-
1
aliases[id][:configurations] += resolve_configurations(options)
-
1
aliases[id][:packages].uniq!
-
1
aliases[id][:modes].uniq!
-
1
aliases[id][:configurations].uniq!
-
else
-
1406
aliases[id] = {
-
packages: resolve_packages(options),
-
modes: resolve_modes(options),
-
configurations: resolve_configurations(options)
-
}
-
1406
Origen.pin_bank.register_alias(id, obj, options)
-
end
-
end
-
-
# Returns true if the pin has the given alias within the given or current context
-
2
def has_alias?(id, options = {})
-
146
if aliases[id]
-
146
if options[:ignore_context]
-
true
-
else
-
146
packages = resolve_packages(options)
-
146
modes = resolve_modes(options)
-
146
configurations = resolve_configurations(options)
-
begin
-
146
aliases[id][:packages].include?(:all) || aliases[id][:packages].empty? ||
-
20
packages.any? { |package| aliases[id][:packages].include?(package) }
-
end && begin
-
132
aliases[id][:modes].include?(:all) || aliases[id][:modes].empty? ||
-
5
modes.any? { |mode| aliases[id][:modes].include?(mode) }
-
end && begin
-
127
aliases[id][:configurations].include?(:all) || aliases[id][:configurations].empty? ||
-
configurations.any? { |config| aliases[id][:configurations].include?(config) }
-
end
-
end
-
else
-
false
-
end
-
end
-
-
# Returns true if the pin is an alias of the given pin name
-
2
def is_alias_of?(name)
-
9
if Origen.pin_bank.find(name)
-
7
Origen.pin_bank.find(name).id == Origen.pin_bank.find(myself).id
-
else
-
2
false
-
end
-
end
-
-
# Returns true if the pin belongs to a pin group.
-
#
-
# add_pins :jtag, size: 6
-
# add_pin :done
-
# add_pin_alias :fail, :jtag, pin: 4
-
#
-
# pin(:done).belongs_to_a_pin_group? # => false
-
# pin(:fail).belongs_to_a_pin_group? # => true
-
2
def belongs_to_a_pin_group?
-
18212
!groups.empty?
-
end
-
-
2
def value
-
14468
@value
-
end
-
2
alias_method :data, :value
-
-
2
def suspend
-
6
invalidate_vector_cache
-
6
@suspend = true
-
end
-
-
2
def suspended?
-
2812
@suspend
-
end
-
-
# Will resume compares on this pin
-
2
def resume
-
2818
invalidate_vector_cache
-
2818
@suspend = false
-
end
-
-
2
def repeat_previous=(bool)
-
228
invalidate_vector_cache
-
228
@repeat_previous = bool
-
end
-
-
2
def repeat_previous?
-
16905
@repeat_previous
-
end
-
-
2
def set_state_with_options(state, options = {})
-
24023
@state_meta = options[:meta] || {}
-
24023
set_state(state)
-
end
-
-
2
def set_state(state)
-
24023
invalidate_vector_cache
-
24023
@repeat_previous = false
-
24023
@state = state
-
end
-
-
2
def set_value(val)
-
16295
orig = val
-
16295
invalidate_vector_cache
-
16295
if val.is_a?(String) || val.is_a?(Symbol)
-
4
val = val.to_s
-
4
if val =~ /^(b|h).+/
-
val = Origen::Value.new(val)
-
else
-
4
@vector_formatted_value = val
-
4
return
-
end
-
end
-
16291
if val.is_a?(Origen::Value)
-
val = val[0]
-
else
-
# If val is a data bit extract the value of it
-
16291
val = val.respond_to?(:data) ? val.data : val
-
# Assume driving/asserting a nil value means 0
-
16291
val = 0 unless val
-
16291
if !val.x_or_z? && val > 1
-
fail "Attempt to set a value of #{val} on pin #{name}"
-
end
-
end
-
16291
@repeat_previous = false
-
16291
if val.x_or_z?
-
dont_care
-
else
-
16291
if inverted?
-
@value = val == 0 ? 1 : 0
-
else
-
16291
@value = val
-
end
-
end
-
end
-
2
alias_method :data=, :set_value
-
-
2
def cycle # :nodoc:
-
82
Origen.tester.cycle
-
end
-
-
2
def state
-
43203
@state
-
end
-
-
2
def state=(value)
-
invalidate_vector_cache
-
@state_meta = {}
-
@state = value
-
end
-
-
# Set the pin to drive a 1 on future cycles
-
2
def drive_hi(options = {})
-
799
set_state_with_options(:drive, options)
-
799
set_value(1)
-
end
-
2
alias_method :write_hi, :drive_hi
-
-
2
def drive_hi!(options = {})
-
drive_hi(options)
-
cycle
-
end
-
2
alias_method :write_hi!, :drive_hi!
-
-
# Set the pin to drive a high voltage on future cycles (if the tester supports it).
-
# For example on a J750 high-voltage channel the pin state would be set to "2"
-
2
def drive_very_hi(options = {})
-
41
set_state_with_options(:drive_very_hi, options)
-
41
set_value(1)
-
end
-
-
2
def drive_very_hi!(options = {})
-
drive_very_hi(options)
-
cycle
-
end
-
-
# Set the pin to drive a 0 on future cycles
-
2
def drive_lo(options = {})
-
3526
set_state_with_options(:drive, options)
-
3526
set_value(0)
-
end
-
2
alias_method :write_lo, :drive_lo
-
-
2
def drive_lo!(options = {})
-
drive_lo(options)
-
cycle
-
end
-
2
alias_method :write_lo!, :drive_lo!
-
-
2
def drive_mem(options = {})
-
45
set_state_with_options(:drive_mem, options)
-
end
-
-
2
def drive_mem!(options = {})
-
2
drive_mem(options)
-
2
cycle
-
end
-
-
2
def expect_mem(options = {})
-
41
set_state_with_options(:expect_mem, options)
-
end
-
-
2
def expect_mem!(options = {})
-
2
expect_mem(options)
-
2
cycle
-
end
-
-
# Set the pin to expect a 1 on future cycles
-
2
def assert_hi(options = {})
-
258
set_state_with_options(:compare, options)
-
258
set_value(1)
-
end
-
2
alias_method :expect_hi, :assert_hi
-
2
alias_method :compare_hi, :assert_hi
-
2
alias_method :read_hi, :assert_hi
-
-
2
def assert_hi!(options = {})
-
assert_hi(options)
-
cycle
-
end
-
2
alias_method :expect_hi!, :assert_hi!
-
2
alias_method :compare_hi!, :assert_hi!
-
2
alias_method :read_hi!, :assert_hi!
-
-
# Set the pin to expect a 0 on future cycles
-
2
def assert_lo(options = {})
-
245
set_state_with_options(:compare, options)
-
245
set_value(0)
-
# Planning to add the active load logic to the tester instead...
-
# options = { :active => false #if active true means to take tester active load capability into account
-
# }.merge(options)
-
# unless state_to_be_inverted?
-
# myself.state = ($tester.active_loads || !options[:active]) ? $tester.pin_state(:expect_lo) : $tester.pin_state(:dont_care)
-
# else
-
# myself.state = ($tester.active_loads || !options[:active]) ? $tester.pin_state(:expect_hi) : $tester.pin_state(:dont_care)
-
# end
-
end
-
2
alias_method :expect_lo, :assert_lo
-
2
alias_method :compare_lo, :assert_lo
-
2
alias_method :read_lo, :assert_lo
-
-
2
def assert_lo!(options = {})
-
assert_lo(options)
-
cycle
-
end
-
2
alias_method :expect_lo!, :assert_lo!
-
2
alias_method :compare_lo!, :assert_lo!
-
2
alias_method :read_lo!, :assert_lo!
-
-
# Set the pin to X on future cycles
-
2
def dont_care(options = {})
-
8100
set_state_with_options(:dont_care, options)
-
end
-
-
2
def dont_care!(options = {})
-
dont_care(options)
-
cycle
-
end
-
-
# Pass in 0 or 1 to have the pin drive_lo or drive_hi respectively.
-
# This is useful when programatically setting the pin state.
-
# ==== Example
-
# [0,1,1,0].each do |level|
-
# $pin(:d_in).drive(level)
-
# end
-
2
def drive(value, options = {})
-
10209
set_state_with_options(:drive, options)
-
10209
set_value(value)
-
end
-
2
alias_method :write, :drive
-
-
2
def drive!(value, options = {})
-
67
drive(value, options)
-
67
cycle
-
end
-
2
alias_method :write!, :drive!
-
-
# Pass in 0 or 1 to have the pin expect_lo or expect_hi respectively.
-
# This is useful when programatically setting the pin state.
-
# ==== Example
-
# [0,1,1,0].each do |level|
-
# $pin(:d_in).assert(level)
-
# end
-
2
def assert(value, options = {})
-
721
set_state_with_options(:compare, options)
-
721
set_value(value)
-
end
-
2
alias_method :compare, :assert
-
2
alias_method :expect, :assert
-
2
alias_method :read, :assert
-
-
2
def assert!(*args)
-
11
assert(*args)
-
11
cycle
-
end
-
2
alias_method :compare!, :assert!
-
2
alias_method :expect!, :assert!
-
2
alias_method :read!, :assert!
-
-
2
def assert_midband(options = {})
-
set_state_with_options(:compare_midband, options)
-
end
-
2
alias_method :compare_midband, :assert_midband
-
2
alias_method :expect_midband, :assert_midband
-
2
alias_method :read_midband, :assert_midband
-
-
2
def assert_midband!(options = {})
-
assert_midband(options)
-
cycle
-
end
-
2
alias_method :compare_midband!, :assert_midband!
-
2
alias_method :expect_midband!, :assert_midband!
-
2
alias_method :read_midband!, :assert_midband!
-
-
# Returns the state of invert
-
2
def inverted?
-
16295
@invert
-
end
-
-
# Returns true if the pin is currently in a compare state
-
2
def comparing?
-
4172
!@suspend &&
-
state == :compare
-
end
-
-
# Returns true if the pin is currently in a compare mem state
-
2
def comparing_mem?
-
3018
!@suspend &&
-
state == :expect_mem
-
end
-
-
# Returns true if the pin is currently in a compare state
-
2
def comparing_midband?
-
4043
!@suspend &&
-
state == :compare_midband
-
end
-
-
# Returns true if the pin is currently in a drive state
-
2
def driving?
-
16806
!@suspend &&
-
16796
(state == :drive || state == :drive_very_hi)
-
end
-
-
# Returns true if the pin is currently in a drive mem state
-
2
def driving_mem?
-
3059
!@suspend &&
-
state == :drive_mem
-
end
-
-
# Returns true if pin is in high voltage state
-
2
def high_voltage?
-
4567
!@suspend &&
-
state == :drive_very_hi
-
end
-
-
2
def toggle
-
516
unless state == :dont_care
-
496
set_value(value == 0 ? 1 : 0)
-
end
-
end
-
-
2
def toggle!
-
toggle
-
cycle
-
end
-
-
# Mark the (data) from the pin to be captured
-
2
def capture(options = {})
-
38
set_state_with_options(:capture, options)
-
end
-
2
alias_method :store, :capture
-
-
# Mark the (data) from the pin to be captured and trigger a cycle
-
2
def capture!(options = {})
-
capture(options)
-
cycle
-
end
-
2
alias_method :store!, :capture!
-
-
# Returns true if the (data) from the pin is marked to be captured
-
2
def to_be_captured?
-
2991
state == :capture
-
end
-
2
alias_method :to_be_stored?, :to_be_captured?
-
2
alias_method :is_to_be_stored?, :to_be_captured?
-
2
alias_method :is_to_be_captured?, :to_be_captured?
-
-
# Restores the state of the pin at the end of the given block
-
# to the state it was in at the start of the block
-
#
-
# pin(:invoke).driving? # => true
-
# pin(:invoke).restore_state do
-
# pin(:invoke).dont_care
-
# pin(:invoke).driving? # => false
-
# end
-
# pin(:invoke).driving? # => true
-
2
def restore_state
-
4
save
-
4
yield
-
4
restore
-
end
-
-
# Saves the current state of the pin, allowing it to be restored to the
-
# current state by calling the restore method
-
2
def save
-
133
@_saved_state << @state
-
133
@_saved_value << @value
-
133
@_saved_suspend << @suspend
-
133
@_saved_invert << @invert
-
133
@_saved_repeat_previous << @repeat_previous
-
end
-
-
# Restores the state of the pin to the last time save was called
-
2
def restore
-
133
invalidate_vector_cache
-
133
@state = @_saved_state.pop
-
133
@value = @_saved_value.pop
-
133
@suspend = @_saved_suspend.pop
-
133
@invert = @_saved_invert.pop
-
133
@repeat_previous = @_saved_repeat_previous.pop
-
end
-
-
2
def is_not_a_clock?
-
16
@clock.nil?
-
end
-
-
2
def is_a_clock?
-
8
!(@clock.nil?)
-
end
-
-
2
def is_a_running_clock?
-
200
@clock.running?
-
end
-
-
2
def enable_clock(options = {})
-
9
@clock = PinClock.new(myself, options)
-
end
-
-
2
def disable_clock(options = {})
-
3
@clock.stop_clock(options)
-
3
@clock = nil
-
end
-
-
2
def update_clock
-
241
@clock.update_clock
-
end
-
-
2
def start_clock(options = {})
-
16
enable_clock(options) if myself.is_not_a_clock?
-
16
@clock.start_clock(options)
-
end
-
2
alias_method :resume_clock, :start_clock
-
-
2
def stop_clock(options = {})
-
14
@clock.stop_clock(options)
-
end
-
2
alias_method :pause_clock, :stop_clock
-
-
2
def next_edge
-
296
@clock.next_edge
-
end
-
-
2
def duty_cycles
-
21
@clock.cycles_per_duty
-
end
-
-
2
def half_period
-
5
@clock.cycles_per_half_period
-
end
-
-
2
def toggle_clock
-
188
fail "ERROR: Clock on #{@owner.name} not running." unless is_a_running_clock?
-
188
@clock.toggle
-
end
-
-
# Delete this pin (myself). Used bang in method name to keep same for pins and
-
# pin collections. Pin collections already had a delete method which deletes
-
# a pin from the collection. Needed delete! to indicate it is deleting the
-
# actual pin or pin group calling the method.
-
2
def delete!
-
2
owner.delete_pin(myself)
-
end
-
-
2
def type=(value)
-
2
if TYPES.include? value
-
2
@type = value
-
else
-
fail "Pin type '#{value}' must be set to one of the following: #{TYPES.join(', ')}"
-
end
-
end
-
-
2
def open_drain=(value)
-
1
if [true, false].include? value
-
1
@open_drain = value
-
else
-
fail "Pin open_drain attribute '#{value}' must be either true or false"
-
end
-
end
-
-
2
def ext_pullup=(value)
-
1
if [true, false].include? value
-
1
@ext_pullup = value
-
else
-
fail "Pin ext_pullup attribute '#{value}' must be either true or false"
-
end
-
end
-
-
2
def ext_pulldown=(value)
-
1
if [true, false].include? value
-
1
@ext_pulldown = value
-
else
-
fail "Pin ext_pulldown attribute '#{value}' must be either true or false"
-
end
-
end
-
-
2
def index?(context: nil)
-
!!index(context: context).nil?
-
end
-
-
2
def index(context: nil)
-
52
if context.is_a?(Symbol)
-
# Context pin group provided
-
16
group = groups[context].instance_variable_get(:@store)
-
16
if group
-
13
group.index(self)
-
end
-
36
elsif context.is_a?(Array)
-
# Anonymous pin group given
-
84
context.map { |p| p.is_a?(Symbol) ? owner.pin(p) : p }.index(self)
-
else
-
# Try an index based off of the pin name.
-
# Only works if the pin ends in a decimal. Otherwise, returns nil.
-
19
i = name.to_s.index(/\d+$/)
-
19
if i
-
16
name.to_s[i..-1].to_i
-
end
-
end
-
end
-
-
2
def mask(context: nil)
-
16
index = context.is_a?(Integer) ? context : self.index(context: context)
-
-
16
if index.nil? && context.nil?
-
# If the index is nil and no context was given, no implicit index could be resolved
-
1
fail("Could not discern pin :#{name}'s implicit index!")
-
15
elsif index.nil?
-
# If the index is nil and some context was given, then the pin is not in the given context
-
1
fail("Pin :#{name} is not a member of the given context!")
-
end
-
-
14
2**index
-
end
-
2
alias_method :set_mask, :mask
-
2
alias_method :smask, :mask
-
-
2
def clear_mask(context: nil, size: nil)
-
23
index = context.is_a?(Integer) ? context : self.index(context: context)
-
-
23
if index.nil? && context.nil?
-
# If the index is nil and no context was given, no implicit index could be resolved
-
1
fail("Could not discern pin :#{name}'s implicit index!")
-
22
elsif index.nil?
-
# If the index is nil and some context was given, then the pin is not in the given context
-
1
fail("Pin :#{name} is not a member of the given context!")
-
end
-
-
21
if size && context && !context.is_a?(Integer)
-
# A context was given, that was not just an Integer, and size was given
-
# Raise an exception as these two conflict.
-
1
fail('Both a sized context (e.g. pin group) and a :size option cannot be used simultaneously!')
-
20
elsif size
-
# A size option was given. Use that.
-
6
((2**size) - 1) ^ (1 << index)
-
14
elsif context.is_a?(Symbol)
-
4
((2**groups[context].instance_variable_get(:@store).size) - 1) ^ (1 << index)
-
10
elsif context.respond_to?(:size) && !context.is_a?(Integer)
-
# PinCollection or Array
-
4
((2**context.size) - 1) ^ (1 << index)
-
else
-
# No size option was given. Use the implicit index instead.
-
6
(2**index) - 1
-
end
-
end
-
2
alias_method :clr_mask, :clear_mask
-
2
alias_method :cmask, :clear_mask
-
-
2
def named?(n)
-
5
if n.is_a?(Regexp)
-
6
[name.to_s, *aliases.keys].any? { |na| na =~ n }
-
else
-
3
[name.to_s, *aliases.keys.map(&:to_s)].include?(n.to_s)
-
end
-
end
-
-
2
def method_missing(m, *args, &block)
-
1
if meta.include? m
-
1
meta[m]
-
else
-
super
-
end
-
end
-
-
2
def respond_to_missing?(m, include_private = false)
-
143
meta[m] || super
-
end
-
-
2
private
-
-
2
def primary_group=(group)
-
3966
@primary_group = group
-
end
-
-
2
def primary_group_index=(number)
-
3966
@primary_group_index = number
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Pins
-
# Stores all pins, pin aliases and pin groups for the current target.
-
# A central store is used to allow either top-level or sub-block objects to
-
# add pins to the current context available to the testers.
-
#
-
# The global Origen pin bank (an instance of this class) is returned from Origen.pin_bank.
-
2
class PinBank
-
2
include Origen::CoreCallbacks
-
-
# There is one pin bank per Origen thread, this clears the bank every time the target is changed
-
#
-
# @api private
-
2
def before_load_target
-
588
empty!
-
end
-
-
# Add the given pin to the bank
-
#
-
# @return [Origen::Pins::Pin] the supplied pin object
-
2
def add_pin(pin, _owner, _options = {})
-
7236
if pin.is_a?(PowerPin)
-
83
bank = all_power_pins
-
7153
elsif pin.is_a?(GroundPin)
-
88
bank = all_ground_pins
-
7065
elsif pin.is_a?(VirtualPin)
-
78
bank = all_virtual_pins
-
6987
elsif pin.is_a?(OtherPin)
-
54
bank = all_other_pins
-
else
-
6933
bank = all_pins
-
end
-
7236
if bank[pin.id]
-
fail "A pin with id #{pin.id} already exists!"
-
end
-
7236
all_ids << pin.id
-
7236
bank[pin.id] = pin
-
# If ends in a number
-
# if !options[:dont_create_group] && pin.id.to_s =~ /(.*?)(\d+)$/
-
# # Create a new group if one with the given name doesn't already exist
-
# unless group = all_pin_groups[$1.to_sym]
-
# group = PinCollection.new(owner, options)
-
# group.id = $1.to_sym
-
# all_pin_groups[$1.to_sym] = group
-
# end
-
# group.add_pin(pin)
-
# end
-
7236
pin
-
end
-
-
2
def add_pin_group(group, owner, options = {})
-
449
unless options[:pins_exist]
-
449
group.each do |pin|
-
3966
add_pin(pin, owner, options.merge(dont_create_group: true))
-
end
-
end
-
449
store_pin_group(group, options)
-
449
group
-
end
-
-
# Returns a hash containing all pins available in the current context stored by their primary ID
-
2
def pins(options = {})
-
1463
all_pins.select do |_id, pin|
-
42643
pin.enabled?(options)
-
end
-
end
-
-
2
def power_pins(options = {})
-
19
all_power_pins.select do |_id, pin|
-
50
pin.enabled?(options)
-
end
-
end
-
-
2
def ground_pins(options = {})
-
20
all_ground_pins.select do |_id, pin|
-
58
pin.enabled?(options)
-
end
-
end
-
-
2
def other_pins(options = {})
-
3
all_other_pins.select do |_id, pin|
-
10
pin.enabled?(options)
-
end
-
end
-
-
2
def virtual_pins(options = {})
-
16
all_virtual_pins.select do |_id, pin|
-
36
pin.enabled?(options)
-
end
-
end
-
-
# Returns a hash containing all pin_groups available in the current context stored by their primary ID
-
2
def pin_groups(options = {})
-
11809
current_pin_group_store(all_pin_groups, options).select do |_id, group|
-
19118
group.enabled?(options)
-
end
-
end
-
-
# Returns a hash containing all power_pin_groups available in the current context stored by their primary ID
-
2
def power_pin_groups(options = {})
-
8
current_pin_group_store(all_power_pin_groups, options).select do |_id, group|
-
7
group.enabled?(options)
-
end
-
end
-
-
# Returns a hash containing all ground_pin_groups available in the current context stored by their primary ID
-
2
def ground_pin_groups(options = {})
-
8
current_pin_group_store(all_ground_pin_groups, options).select do |_id, group|
-
7
group.enabled?(options)
-
end
-
end
-
-
# Returns a hash containing all ground_pin_groups available in the current context stored by their primary ID
-
2
def other_pin_groups(options = {})
-
4
current_pin_group_store(all_other_pin_groups, options).select do |_id, group|
-
3
group.enabled?(options)
-
end
-
end
-
-
# Returns a hash containing all virtual_pin_groups available in the current context stored by their primary ID
-
2
def virtual_pin_groups(options = {})
-
4
current_pin_group_store(all_virtual_pin_groups, options).select do |_id, group|
-
3
group.enabled?(options)
-
end
-
end
-
-
# Returns a hash containing all pins stored by their primary ID
-
2
def all_pins
-
20236
@all_pins ||= {}
-
end
-
-
# Returns a hash containing all pin groups stored by context
-
2
def all_pin_groups
-
19307
@all_pin_groups ||= {}
-
end
-
-
# Returns a hash containing all power pins stored by their primary ID
-
2
def all_power_pins
-
153
@all_power_pins ||= {}
-
end
-
-
# Returns a hash containing all ground pins stored by their primary ID
-
2
def all_ground_pins
-
159
@all_ground_pins ||= {}
-
end
-
-
# Returns a hash containing all other pins stored by their primary ID
-
2
def all_other_pins
-
74
@all_other_pins ||= {}
-
end
-
-
# Returns a hash containing all virtual pins stored by their primary ID
-
2
def all_virtual_pins
-
111
@all_virtual_pins ||= {}
-
end
-
-
# Returns a hash containing all power pin groups stored by context
-
2
def all_power_pin_groups
-
39
@all_power_pin_groups ||= {}
-
end
-
-
# Returns a hash containing all ground pin groups stored by context
-
2
def all_ground_pin_groups
-
40
@all_ground_pin_groups ||= {}
-
end
-
-
# Returns a hash containing all other pin groups stored by context
-
2
def all_other_pin_groups
-
12
@all_other_pin_groups ||= {}
-
end
-
-
# Returns a hash containing all virtual pin groups stored by context
-
2
def all_virtual_pin_groups
-
10
@all_virtual_pin_groups ||= {}
-
end
-
-
2
def find(id, options = {})
-
11973
id = id.to_sym
-
11973
if options[:power_pin]
-
51
pin = all_power_pins[id] || find_pin_group(id, options)
-
11922
elsif options[:ground_pin]
-
51
pin = all_ground_pins[id] || find_pin_group(id, options)
-
11871
elsif options[:virtual_pin]
-
17
pin = all_virtual_pins[id] || find_pin_group(id, options)
-
11854
elsif options[:other_pin]
-
17
pin = all_other_pins[id] || find_pin_group(id, options)
-
else
-
11837
pin = all_pins[id] || find_by_alias(id, options) || find_pin_group(id, options)
-
end
-
11973
if pin
-
11951
if options[:ignore_context] || pin.enabled?(options)
-
11941
pin
-
end
-
end
-
end
-
-
2
def find_pin_group(id, options = {})
-
options = {
-
6460
include_all: true
-
}.merge(options)
-
6460
if options[:power_pin]
-
17
base = all_power_pin_groups
-
6443
elsif options[:ground_pin]
-
18
base = all_ground_pin_groups
-
6425
elsif options[:other_pin]
-
6
base = all_other_pin_groups
-
6419
elsif options[:virtual_pin]
-
4
base = all_virtual_pin_groups
-
else
-
6415
base = all_pin_groups
-
end
-
6460
pin_group_stores_in_context(base, options) do |store|
-
6474
return store[id] if store[id]
-
end
-
nil
-
end
-
-
# This will be called by the pins whenever a new alias is added to them
-
2
def register_alias(id, pin, _options = {})
-
1406
known_aliases[id] ||= []
-
1406
known_aliases[id] << pin
-
end
-
-
# Find an existing pin group with the given ID if it exists and return it, otherwise create one
-
2
def find_or_create_pin_group(id, owner, options = {})
-
668
group = find_pin_group(id, options)
-
668
unless group
-
664
group = PinCollection.new(owner, options)
-
664
group.id = id
-
664
store_pin_group(group, options)
-
end
-
668
group
-
end
-
-
# Delete a specific pin
-
2
def delete_pin(pin)
-
3
if pin.is_a?(PowerPin)
-
bank = all_power_pins
-
3
elsif pin.is_a?(GroundPin)
-
bank = all_ground_pins
-
3
elsif pin.is_a?(OtherPin)
-
bank = all_other_pins
-
3
elsif pin.is_a?(VirtualPin)
-
bank = all_virtual_pins
-
else
-
3
bank = all_pins
-
end
-
# First delete the pin from any of the pin groups it resides
-
3
Origen.pin_bank.pin_groups.each do |_name, grp|
-
1
next unless grp.store.include?(pin)
-
1
grp.delete(pin)
-
end
-
# Now delete the pin from the pin bank
-
3
if bank[pin.id]
-
3
bank.delete(pin.id)
-
# Delete all known aliases as well
-
3
known_aliases.delete(pin.name)
-
else
-
if pin.id == pin.name
-
fail "A pin with id #{pin.id} does not exist!"
-
else
-
fail "A pin with id #{pin.id} and name #{pin.name} does not exist!"
-
end
-
end
-
end
-
-
# Delete a specific pin group
-
2
def delete_pingroup(group)
-
2
found_group = false
-
2
if group.power_pins?
-
base = all_power_pin_groups
-
2
elsif group.ground_pins?
-
base = all_ground_pin_groups
-
2
elsif group.other_pins?
-
base = all_other_pin_groups
-
2
elsif group.virtual_pins?
-
base = all_virtual_pin_groups
-
else
-
2
base = all_pin_groups
-
end
-
2
pin_group_stores_in_context(base) do |store|
-
2
if store.include?(group.id)
-
2
store.delete(group.id)
-
2
found_group = true
-
end
-
end
-
2
fail "A pin group with id #{group.id} does not exist!" unless found_group == true
-
end
-
-
2
private
-
-
2
def current_pin_group_store(base, options)
-
11833
pin_group_stores_in_context(base, options)
-
end
-
-
2
def pin_group_stores_in_context(base, options = {})
-
# Pin group availability is now only scoped by package
-
19408
options[:mode] = :all
-
19408
options[:configuration] = :all
-
19408
resolve_packages(options).each do |package|
-
19422
base[package] ||= {}
-
19422
resolve_modes(options).each do |mode|
-
19422
base[package][mode] ||= {}
-
19422
resolve_configurations(options).each do |config|
-
19422
base[package][mode][config] ||= {}
-
19422
if block_given?
-
7589
yield base[package][mode][config]
-
else
-
11833
return base[package][mode][config]
-
end
-
end
-
end
-
end
-
end
-
-
2
def store_pin_group(group, options)
-
1113
if group.power_pins?
-
14
base = all_power_pin_groups
-
1099
elsif group.ground_pins?
-
14
base = all_ground_pin_groups
-
1085
elsif group.other_pins?
-
2
base = all_other_pin_groups
-
1083
elsif group.virtual_pins?
-
2
base = all_virtual_pin_groups
-
else
-
1081
base = all_pin_groups
-
end
-
1113
pin_group_stores_in_context(base, options) do |store|
-
1113
store[group.id] = group
-
end
-
end
-
-
# Returns an array containing the package ids resolved from the given options or
-
# the current top-level context
-
2
def resolve_packages(options = {})
-
19408
p = [options.delete(:package) || options.delete(:packages) || current_package_id].flatten.compact
-
19408
if options[:include_all] || p.empty?
-
19378
p << :all
-
end
-
19408
p.uniq
-
end
-
-
# Returns an array containing the mode ids resolved from the given options or
-
# the current top-level context
-
2
def resolve_modes(options = {})
-
19422
m = [options.delete(:mode) || options.delete(:modes) || current_mode_id].flatten.compact
-
19422
if options[:include_all] || m.empty?
-
6474
m << :all
-
end
-
19422
m.uniq
-
end
-
-
# Returns an array containing the configuration ids resolved from the given options or
-
# the current top-level context
-
2
def resolve_configurations(options = {})
-
19422
c = [options.delete(:configuration) || options.delete(:configurations) || current_configuration].flatten.compact
-
19422
if options[:include_all] || c.empty?
-
6474
c << :all
-
end
-
19422
c.uniq
-
end
-
-
# Returns the current configuration context for this pin/pin group, if a configuration has been
-
# explicitly set on this pin that will be returned, otherwise the current chip-level configuration
-
# context will be returned (nil if none is set)
-
2
def current_configuration
-
14
if Origen.top_level
-
14
Origen.top_level.current_configuration
-
end
-
end
-
-
# Returns the current top-level package ID, nil if none is set.
-
2
def current_package_id
-
19383
if Origen.top_level && Origen.top_level.current_package
-
33
Origen.top_level.current_package.id
-
end
-
end
-
-
# Returns the current top-level mode ID, nil if none is set.
-
2
def current_mode_id
-
14
if Origen.top_level && Origen.top_level.current_mode
-
Origen.top_level.current_mode.id
-
end
-
end
-
-
2
def find_by_alias(id, options = {})
-
5903
if known_aliases[id]
-
140
pins = known_aliases[id].select do |pin|
-
145
pin.has_alias?(id, options)
-
end
-
140
if pins.size > 1
-
fail "Mutliple pins with the alias #{id} have been found in the current scope!"
-
end
-
140
pins.first
-
end
-
end
-
-
# Delete all pins, groups and aliases from the bank
-
2
def empty!
-
589
@all_ids = nil
-
589
@known_aliases = nil
-
589
@all_pins = nil
-
589
@all_power_pins = nil
-
589
@all_ground_pins = nil
-
589
@all_other_pins = nil
-
589
@all_virtual_pins = nil
-
589
@all_pin_groups = nil
-
589
@all_power_pin_groups = nil
-
589
@all_ground_pin_groups = nil
-
589
@all_other_pin_groups = nil
-
589
@all_virtual_pin_groups = nil
-
end
-
-
2
def known_aliases
-
8858
@known_aliases ||= {}
-
end
-
-
2
def all_ids
-
7236
@all_ids ||= []
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Pins
-
2
class PinClock
-
2
attr_reader :cycles_per_duty, :last_edge, :next_edge
-
-
2
def initialize(owner, options = {})
-
9
@owner = owner
-
9
@running = false
-
-
9
@clock_period_in_ns = 0
-
9
@tester_period_in_ns = 0
-
-
9
update_clock_period(options)
-
9
update_tester_period_local
-
9
update_clock_parameters
-
end
-
-
2
def start_clock(options = {})
-
# Throw error if this pin is already a running clock
-
16
if running?
-
fail "PIN CLOCK ERROR: Clock on #{@owner.name} already running."
-
end
-
-
16
clock_updated = update_clock_period(options)
-
16
tester_updated = update_tester_period_local
-
16
if clock_updated || tester_updated
-
4
update_clock_parameters
-
end
-
-
16
cc "[PinClock] Start #{@owner.name}.clock at #{Origen.tester.cycle_count}: period=#{@clock_period_in_ns}ns, cycles=#{cycles_per_period}, duty=#{duty_str}"
-
16
update_edges
-
16
Origen.tester.push_running_clock(@owner) unless running?
-
16
@running = true
-
end
-
-
2
def stop_clock(options = {})
-
17
cc "[PinClock] Stop #{@owner.name}.clock: stop_cycle=#{Origen.tester.cycle_count}" if running?
-
17
Origen.tester.pop_running_clock(@owner) if running?
-
17
@running = false
-
end
-
-
2
def restart_clock
-
stop_clock
-
update_clock
-
start_clock
-
end
-
-
2
def update_clock
-
241
if update_tester_period_local
-
5
update_clock_parameters
-
5
cc "[PinClock] Update #{@owner.name}.clock at #{Origen.tester.cycle_count}: period=#{@clock_period_in_ns}ns, cycles=#{cycles_per_period}, duty=#{duty_str}"
-
5
update_edges
-
end
-
end
-
-
2
def running?
-
266
@running
-
end
-
-
2
def toggle
-
188
@owner.toggle
-
188
update_edges
-
end
-
-
# The only caller to this should be legacy support so just force 50% duty cycle
-
2
def cycles_per_half_period
-
5
@cycles_per_duty.min
-
end
-
-
2
private
-
-
2
def update_clock_parameters
-
18
@cycles_per_duty = [(cycles_per_period / 2.0).floor, (cycles_per_period / 2.0).ceil]
-
end
-
-
2
def cycles_per_period
-
57
(@clock_period_in_ns / @tester_period_in_ns).to_int
-
end
-
-
2
def update_edges
-
209
@last_edge = Origen.tester.cycle_count
-
209
@next_edge = Origen.tester.cycle_count + @cycles_per_duty[0]
-
209
@cycles_per_duty.reverse!
-
end
-
-
2
def update_tester_period_local
-
266
if Origen.tester.current_period_in_ns == @tester_period_in_ns
-
250
return false
-
else
-
16
@tester_period_in_ns = Origen.tester.current_period_in_ns
-
16
return true
-
end
-
end
-
-
2
def update_clock_period(options = {})
-
25
new = get_clock_period(options)
-
-
25
if new == @clock_period_in_ns
-
14
false
-
else
-
11
@clock_period_in_ns = new
-
11
true
-
end
-
end
-
-
2
def get_clock_period(options = {})
-
25
return @clock_period_in_ns if options.empty?
-
-
11
p = []
-
-
# Passed in as time
-
11
p << (options[:period_in_s] * 1_000_000_000) if options[:period_in_s]
-
11
p << (options[:period_in_ms] * 1_000_000) if options[:period_in_ms]
-
11
p << (options[:period_in_us] * 1_000) if options[:period_in_us]
-
11
p << (options[:period_in_ns] * 1) if options[:period_in_ns]
-
-
# Passed in as frequency (or freq.)
-
11
p << ((1.0 / options[:frequency_in_hz]) * 1_000_000_000) if options[:frequency_in_hz]
-
11
p << ((1.0 / options[:freq_in_hz]) * 1_000_000_000) if options[:freq_in_hz]
-
11
p << ((1.0 / options[:frequency_in_khz]) * 1_000_000) if options[:frequency_in_khz]
-
11
p << ((1.0 / options[:freq_in_khz]) * 1_000_000) if options[:freq_in_khz]
-
11
p << ((1.0 / options[:frequency_in_mhz]) * 1_000) if options[:frequency_in_mhz]
-
11
p << ((1.0 / options[:freq_in_mhz]) * 1_000) if options[:freq_in_mhz]
-
-
# Passed in as cycles (not advised)
-
11
p << (options[:cycles] * Origen.tester.period_in_ns) if options[:cycles]
-
-
11
return @clock_period_in_ns if p.empty?
-
11
fail "[Pin Clock] ERROR: Multiple unit declarations for #{@owner.name}.clock" if p.size > 1
-
11
p[0].to_int
-
end
-
-
2
def duty_str
-
21
"#{@cycles_per_duty[0]}/#{@cycles_per_duty[1]}"
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Pins
-
# A class that is used to wrap collections of one or more pins. Anytime a group
-
# of pins is fetched or returned by the Pin API it will be wrapped in a PinCollection.
-
2
class PinCollection
-
2
include PinCommon
-
2
include Enumerable
-
2
include OrgFile::Interceptable
-
-
2
ORG_FILE_INTERCEPTED_METHODS = [
-
:drive, :write, :drive_hi, :write_hi, :drive_lo, :write_lo, :drive_very_hi, :drive_mem, :expect_mem, :toggle,
-
:repeat_previous=, :capture, :assert, :read, :compare, :expect,
-
:assert_hi, :expect_hi, :compare_hi, :read_hi, :assert_lo, :expect_lo, :compare_lo, :read_lo,
-
:dont_care
-
]
-
-
2
attr_accessor :endian
-
2
attr_accessor :description
-
2
attr_accessor :group
-
2
attr_accessor :group_str
-
2
attr_accessor :color
-
-
2
def initialize(owner, *pins)
-
1324
options = pins.last.is_a?(Hash) ? pins.pop : {}
-
options = {
-
1324
endian: :big
-
}.merge(options)
-
1324
@power_pins = options.delete(:power_pin) || options.delete(:power_pins)
-
1324
@ground_pins = options.delete(:ground_pin) || options.delete(:ground_pins)
-
1324
@virtual_pins = options.delete(:virtual_pin) || options.delete(:virtual_pins)
-
1324
@other_pins = options.delete(:other_pin) || options.delete(:other_pins)
-
1324
@endian = options[:endian]
-
1324
@rtl_name = options[:rtl_name]
-
1324
@description = options[:description] || options[:desc]
-
1324
@options = options
-
1324
@store = []
-
1324
pins.each_with_index do |pin, i|
-
38
@store[i] = pin
-
end
-
1324
on_init(owner, options)
-
end
-
-
2
def rtl_name
-
2
(@rtl_name || id).to_s
-
end
-
-
2
def global_path_to
-
"dut.pins(:#{id})"
-
end
-
-
2
def org_file_intercepted_methods
-
ORG_FILE_INTERCEPTED_METHODS
-
end
-
-
# Returns the value held by the pin group as a string formatted to the current tester's pattern syntax
-
#
-
# @example
-
#
-
# pin_group.drive_hi
-
# pin_group.to_vector # => "11111111"
-
# pin_group.expect_lo
-
# pin_group.to_vector # => "LLLLLLLL"
-
2
def to_vector
-
42023
return @vector_formatted_value if @vector_formatted_value
-
508
vals = map(&:to_vector)
-
508
vals.reverse! if endian == :little
-
508
@vector_formatted_value = vals.join('')
-
end
-
-
# @api private
-
2
def invalidate_vector_cache
-
13716
@vector_formatted_value = nil
-
end
-
-
# Set the values and states of the pin group's pins from a string formatted to the current tester's pattern syntax,
-
# this is the opposite of the to_vector method
-
#
-
# @example
-
#
-
# pin_group.vector_formatted_value = "LLLLLLLL"
-
# pin_group[0].driving? # => false
-
# pin_group[0].value # => 0
-
# pin_group.vector_formatted_value = "HHHH1111"
-
# pin_group[0].driving? # => true
-
# pin_group[0].value # => 1
-
# pin_group[7].driving? # => false
-
# pin_group[7].value # => 1
-
2
def vector_formatted_value=(val)
-
6
unless @vector_formatted_value == val
-
6
unless val.size == size
-
fail 'When setting vector_formatted_value on a pin group you must supply values for all pins!'
-
end
-
6
val.split(//).reverse.each_with_index do |val, i|
-
26
myself[i].vector_formatted_value = val
-
end
-
6
@vector_formatted_value = val
-
end
-
end
-
-
# Returns true if the pin collection contains power pins rather than regular pins
-
2
def power_pins?
-
4893
@power_pins
-
end
-
-
# Returns true if the pin collection contains ground pins rather than regular pins
-
2
def ground_pins?
-
4850
@ground_pins
-
end
-
-
# Returns true if the pin collection contains virtual pins rather than regular pins
-
2
def virtual_pins?
-
4788
@virtual_pins
-
end
-
-
# Returns true if the pin collection contains other pins rather than regular pins
-
2
def other_pins?
-
4795
@other_pins
-
end
-
-
2
def id
-
7490
@id
-
end
-
-
# Explicitly set the name of a pin group/collection
-
2
def name=(val)
-
@name = val
-
end
-
-
2
def name
-
2379
@name || id
-
end
-
-
# Overrides the regular Ruby array each to be endian aware. If the pin collection/group is
-
# defined as big endian then this will yield the least significant pin first, otherwise for
-
# little endian the most significant pin will come out first.
-
2
def each
-
12575
size.times do |i|
-
112971
if endian == :big
-
83953
yield @store[size - i - 1]
-
else
-
29018
yield @store[i]
-
end
-
end
-
end
-
-
2
def size
-
99443
@store.size
-
end
-
-
2
def [](*indexes)
-
8150
if indexes.size > 1 || indexes.first.is_a?(Range)
-
199
p = PinCollection.new(owner, @options)
-
199
expand_and_order(indexes).each do |index|
-
792
p << @store[index]
-
end
-
199
p
-
else
-
7951
@store[indexes.first]
-
end
-
end
-
-
2
def sort!(&block)
-
2
@store = sort(&block)
-
2
myself
-
end
-
-
2
def sort_by!
-
@store = sort_by
-
myself
-
end
-
-
2
def []=(index, pin)
-
3966
@store[index] = pin
-
end
-
-
# Describe the pin group contents. Default is to display pin.id but passing in
-
# :name will display pin.name
-
2
def describe(display = :id)
-
desc = ['********************']
-
desc << "Group id: #{id}"
-
desc << "\nDescription: #{description}" if description
-
desc << "\nEndianness: #{endian}"
-
-
unless size == 0
-
desc << ''
-
desc << 'Pins'
-
desc << '-------'
-
if display == :id
-
desc << map(&:id).join(', ')
-
elsif display == :name
-
desc << map(&:name).join(', ')
-
else
-
fail 'Error: Argument options for describe method are :id and :name. Default is :id'
-
end
-
end
-
-
desc << '********************'
-
puts desc.join("\n")
-
end
-
-
2
def add_pin(pin, _options = {})
-
3961
if pin.is_a?(PinCollection)
-
# Need this to bypass the endianness aware iteration, the storing order
-
# is always the same. So can't use each and co here.
-
183
pin.size.times do |i|
-
732
pin[i].invalidate_group_cache
-
732
@store.push(pin[i])
-
end
-
else
-
# Convert any named reference to a pin object
-
3778
if power_pins?
-
29
pin = owner.power_pins(pin)
-
3749
elsif ground_pins?
-
41
pin = owner.ground_pins(pin)
-
3708
elsif other_pins?
-
5
pin = owner.other_pins(pin)
-
3703
elsif virtual_pins?
-
5
pin = owner.virtual_pins(pin)
-
else
-
3698
pin = owner.pins(pin)
-
end
-
3778
unless @store.include?(pin)
-
3778
pin.invalidate_group_cache
-
3778
@store.push(pin)
-
end
-
end
-
end
-
2
alias_method :<<, :add_pin
-
-
2
def drive(val)
-
215
value = clean_value(value)
-
215
each_with_index do |pin, i|
-
1652
pin.drive(val[size - i - 1])
-
end
-
215
myself
-
end
-
2
alias_method :write, :drive
-
-
2
def drive!(val)
-
145
drive(val)
-
145
cycle
-
end
-
2
alias_method :write!, :drive!
-
-
# Set all pins in pin group to drive 1's on future cycles
-
2
def drive_hi
-
7
each(&:drive_hi)
-
7
myself
-
end
-
2
alias_method :write_hi, :drive_hi
-
-
2
def drive_hi!
-
4
drive_hi
-
4
cycle
-
end
-
2
alias_method :write_hi!, :drive_hi!
-
-
# Set all pins in pin group to drive 0's on future cycles
-
2
def drive_lo
-
11
each(&:drive_lo)
-
11
myself
-
end
-
2
alias_method :write_lo, :drive_lo
-
-
2
def drive_lo!
-
4
drive_lo
-
4
cycle
-
end
-
2
alias_method :write_lo!, :drive_lo!
-
-
# Set all pins in the pin group to drive a high voltage on future cycles (if the tester supports it).
-
# For example on a J750 high-voltage channel the pin state would be set to "2"
-
2
def drive_very_hi
-
5
each(&:drive_very_hi)
-
5
myself
-
end
-
-
2
def drive_very_hi!
-
4
drive_very_hi
-
4
cycle
-
end
-
-
2
def drive_mem
-
5
each(&:drive_mem)
-
5
myself
-
end
-
-
2
def drive_mem!
-
2
drive_mem
-
2
cycle
-
end
-
-
2
def expect_mem
-
5
each(&:expect_mem)
-
5
myself
-
end
-
-
2
def expect_mem!
-
2
expect_mem
-
2
cycle
-
end
-
-
# Returns the data value held by the collection
-
# ==== Example
-
# pins(:porta).write(0x55)
-
# pins(:porta).data # => 0x55
-
2
def data
-
29
data = 0
-
233
each_with_index { |pin, i| data |= pin.data << (size - i - 1) }
-
29
data
-
end
-
2
alias_method :val, :data
-
2
alias_method :value, :data
-
-
# Returns the inverse of the data value held by the collection
-
2
def data_b
-
# (& operation takes care of Bignum formatting issues)
-
6
~data & ((1 << size) - 1)
-
end
-
-
2
def toggle
-
41
each(&:toggle)
-
41
myself
-
end
-
-
2
def toggle!
-
9
toggle
-
9
cycle
-
end
-
-
2
def repeat_previous=(bool)
-
4
each { |pin| pin.repeat_previous = bool }
-
1
myself
-
end
-
-
# Mark the (data) from all the pins in the pin group to be captured
-
2
def capture
-
5
each(&:capture)
-
5
myself
-
end
-
2
alias_method :store, :capture
-
-
2
def capture!
-
capture
-
cycle
-
end
-
2
alias_method :store!, :capture!
-
-
2
def restore_state
-
3
save
-
3
yield
-
3
restore
-
end
-
-
2
def save
-
3
each(&:save)
-
end
-
-
2
def restore
-
3
each(&:restore)
-
end
-
-
2
def id=(val)
-
1113
@id = val.to_sym
-
end
-
-
2
def cycle
-
189
Origen.tester.cycle
-
end
-
-
2
def assert(value, options = {})
-
26
value = clean_value(value)
-
26
each_with_index do |pin, i|
-
184
if !value.respond_to?('data')
-
176
pin.assert(value[size - i - 1], options)
-
8
elsif value[size - i - 1].is_to_be_read?
-
4
pin.assert(value[size - i - 1].data, options)
-
else
-
4
pin.dont_care
-
end
-
end
-
26
myself
-
end
-
2
alias_method :compare, :assert
-
2
alias_method :expect, :assert
-
2
alias_method :read, :assert
-
-
2
def assert!(*args)
-
7
assert(*args)
-
7
cycle
-
end
-
2
alias_method :compare!, :assert!
-
2
alias_method :expect!, :assert!
-
2
alias_method :read!, :assert!
-
-
# Set all pins in the pin group to expect 1's on future cycles
-
2
def assert_hi(options = {})
-
54
each { |pin| pin.assert_hi(options) }
-
6
myself
-
end
-
2
alias_method :expect_hi, :assert_hi
-
2
alias_method :compare_hi, :assert_hi
-
2
alias_method :read_hi, :assert_hi
-
-
2
def assert_hi!
-
4
assert_hi
-
4
cycle
-
end
-
2
alias_method :expect_hi!, :assert_hi!
-
2
alias_method :compare_hi!, :assert_hi!
-
2
alias_method :read_hi!, :assert_hi!
-
-
# Set all pins in the pin group to expect 0's on future cycles
-
2
def assert_lo(options = {})
-
54
each { |pin| pin.assert_lo(options) }
-
6
myself
-
end
-
2
alias_method :expect_lo, :assert_lo
-
2
alias_method :compare_lo, :assert_lo
-
2
alias_method :read_lo, :assert_lo
-
-
2
def assert_lo!
-
4
assert_lo
-
4
cycle
-
end
-
2
alias_method :expect_lo!, :assert_lo!
-
2
alias_method :compare_lo!, :assert_lo!
-
2
alias_method :read_lo!, :assert_lo!
-
-
# Set all pins in the pin group to X on future cycles
-
2
def dont_care
-
7
each(&:dont_care)
-
7
myself
-
end
-
-
2
def dont_care!
-
4
dont_care
-
4
cycle
-
end
-
-
2
def inverted?
-
2
all?(&:inverted?)
-
end
-
-
2
def comparing?
-
4
all?(&:comparing?)
-
end
-
-
2
def comparing_mem?
-
2
all?(&:comparing_mem?)
-
end
-
-
2
def driving?
-
4
all?(&:driving?)
-
end
-
-
2
def driving_mem?
-
1
all?(&:driving_mem?)
-
end
-
-
2
def high_voltage?
-
1
all?(&:high_voltage?)
-
end
-
-
2
def repeat_previous?
-
2
all?(&:repeat_previous?)
-
end
-
-
# Returns true if the (data) from the pin collection is marked to be captured
-
2
def to_be_captured?
-
4
all?(&:to_be_captured?)
-
end
-
2
alias_method :to_be_stored?, :to_be_captured?
-
2
alias_method :is_to_be_stored?, :to_be_captured?
-
2
alias_method :is_to_be_captured?, :to_be_captured?
-
-
# Deletes all occurrences of a pin in a pin group
-
2
def delete(p)
-
1
@store.delete(p)
-
end
-
-
# Deletes the pin at a particular numeric index within the pin group
-
2
def delete_at(index)
-
@store.delete_at(index)
-
end
-
-
2
def pins(nick = :id)
-
1
Origen.deprecate <<-END
-
The PinCollection#pins method is deprecated, if you want to get a list of pin IDs
-
in the given collection just do pins(:some_group).map(&:id)
-
Note that the pins method (confusingly) also does a sort, to replicate that:
-
pins(:some_group).map(&:id).sort
-
END
-
1
if nick == :id
-
1
@store.map(&:id).sort
-
elsif nick == :name
-
@store.map(&:name).sort
-
end
-
end
-
-
# Delete this pingroup (myself)
-
2
def delete!
-
1
owner.delete_pin(myself)
-
end
-
-
2
private
-
-
2
def clean_value(val)
-
241
return val if val.respond_to?(:contains_bits?)
-
240
val = val.data if val.respond_to?('data')
-
240
if val.is_a?(String) || val.is_a?(Symbol)
-
Origen::Value.new(val)
-
else
-
240
val
-
end
-
end
-
-
# Cleans up indexed references to pins, e.g. makes these equal:
-
#
-
# pins(:pb)[0,1,2,3]
-
# pins(:pb)[3,2,1,0]
-
# pins(:pb)[0..3]
-
# pins(:pb)[3..0]
-
2
def expand_and_order(*indexes)
-
199
ixs = []
-
199
indexes.flatten.each do |index|
-
216
if index.is_a?(Range)
-
192
if index.first > index.last
-
191
ixs << (index.last..index.first).to_a
-
else
-
1
ixs << index.to_a
-
end
-
else
-
24
ixs << index
-
end
-
end
-
199
ixs.flatten.sort
-
end
-
-
2
def method_missing(method, *args, &block)
-
# Where the collection is only comprised of one pin delegate missing methods/attributes
-
# to that pin
-
6
if size == 1
-
first.send(method, *args, &block)
-
# Send all assignment methods to all contained pins
-
6
elsif method.to_s =~ /.*=$/
-
2
each do |pin|
-
16
pin.send(method, *args, &block)
-
end
-
else
-
4
if block_given?
-
fail 'Blocks are not currently supported by pin collections containing multiple pins!'
-
else
-
# Allow getters if all pins are the same
-
4
ref = first.send(method, *args)
-
36
if myself.all? { |pin| pin.send(method, *args) == ref }
-
4
ref
-
else
-
fail "The pins held by pin collection #{id} have different values for #{method}"
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Pins
-
# Methods and attributes that are common to both pins
-
# and pin groups
-
2
module PinCommon
-
2
extend ActiveSupport::Concern
-
-
2
included do
-
4
attr_reader :id
-
4
attr_reader :owner
-
# Returns a hash containing the chip packages that the given pin is present in and a metadata hash for each package option containing
-
# information like the location or pin number for the given in pin in the given package.
-
4
attr_reader :packages
-
# Returns a hash containing the chip modes that the given pin is present in and a metadata hash for storing any information
-
# specific to the operation of the given pin in that mode
-
4
attr_reader :modes
-
# Returns a hash containing the chip configurations that the given pin is present in and a metadata hash for storing any information
-
# specific to the operation of the given pin in that configuration
-
4
attr_reader :configurations
-
# Override the chip-level configuration attribute for the given pin
-
4
attr_accessor :configuration
-
# Free format field to store an description of the pin or pin group function
-
4
attr_accessor :description
-
end
-
-
2
def to_sym
-
3056
id
-
end
-
-
# The ID of a pin should be considered immutable, however internally it may be neccessary
-
# to change the initial ID as the pins are initially setup
-
#
-
# @api private
-
2
def id=(val)
-
3967
if @id && @finalized
-
fail 'The ID of a pin cannot be changed once it has been set!'
-
else
-
3967
@id = val
-
end
-
end
-
-
# @api private
-
2
def finalize
-
7236
@finalized = true
-
end
-
-
# Returns true if the pin is enabled by the current or given context
-
2
def enabled?(options = {})
-
73886
present_in_package?(options) # && enabled_in_mode?(options) && enabled_in_configuration?(options)
-
end
-
-
# Returns true if the pin or pin group is present in the current package context.
-
#
-
# A pin is considered enabled when either no package context is set (all pins available
-
# at die level), or when a package context is set and it matches one attached to the pin
-
2
def enabled_in_package?(options = {})
-
73886
package = options[:package] || current_package_id
-
73886
if package
-
108
!!(packages[:all] || packages[package])
-
else
-
73778
true
-
end
-
end
-
2
alias_method :present_in_package?, :enabled_in_package?
-
-
# Returns true if the pin or pin group is present in the current mode context.
-
2
def enabled_in_mode?(options = {})
-
mode = options[:mode] || current_mode_id
-
if mode
-
!!(modes[:all] || modes.empty? || modes[mode])
-
# If no mode is specified a pin is only available if it does not have a mode constraint
-
else
-
!!(modes[:all] || modes.empty?)
-
end
-
end
-
-
# Returns true if the pin or pin group is present in the current configuration context.
-
2
def enabled_in_configuration?(options = {})
-
config = options[:configuration] || current_configuration
-
if config
-
!!(configurations[:all] || configurations.empty? || configurations[config])
-
# If no configuration is specified a pin is only available if it does not have a configuration constraint
-
else
-
!!(configurations[:all] || configurations.empty?)
-
end
-
end
-
-
# Make the pin available in the given package, any options that are supplied will be
-
# saved as metadata associated with the given pin in that package
-
2
def add_package(id, options = {})
-
65
packages[id] = options
-
65
if is_a?(Pin)
-
55
add_location(options[:location], package: id) if options[:location]
-
end
-
end
-
-
# Make the pin or pin group available in the given mode, any options that are supplied will be
-
# saved as metadata associated with the given pin in that mode
-
2
def add_mode(id, options = {})
-
2
modes[id] = options
-
end
-
-
# Make the pin or pin group available in the given configuration, any options that are supplied will be
-
# saved as metadata associated with the given pin in that configuration
-
2
def add_configuration(id, options = {})
-
configurations[id] = options
-
end
-
-
2
private
-
-
2
def on_init(owner, options = {})
-
8560
@owner = owner
-
8560
@description = options[:description]
-
8560
apply_initial_scope(options)
-
end
-
-
# Returns the current configuration context for this pin/pin group, if a configuration has been
-
# explicitly set on this pin that will be returned, otherwise the current chip-level configuration
-
# context will be returned (nil if none is set)
-
2
def current_configuration
-
10128
configuration || begin
-
10126
if Origen.top_level
-
10126
Origen.top_level.current_configuration
-
end
-
end
-
end
-
-
# Returns the current top-level package ID, nil if none is set.
-
2
def current_package_id
-
83908
if Origen.top_level && Origen.top_level.current_package
-
156
Origen.top_level.current_package.id
-
end
-
end
-
-
# Returns the current top-level mode ID, nil if none is set.
-
2
def current_mode_id
-
13681
if Origen.top_level && Origen.top_level.current_mode
-
30
Origen.top_level.current_mode.id
-
end
-
end
-
-
2
def apply_initial_scope(options)
-
8560
@packages = {}
-
8560
@modes = {}
-
8560
@configurations = {}
-
8560
add_initial_packages(options)
-
8560
add_initial_modes(options)
-
8560
add_initial_configurations(options)
-
end
-
-
# Returns an array containing the package ids resolved from the given options or
-
# the current top-level context
-
2
def resolve_packages(options = {})
-
10147
[options.delete(:package) || options.delete(:packages) || current_package_id].flatten.compact
-
end
-
-
# Returns an array containing the mode ids resolved from the given options or
-
# the current top-level context
-
2
def resolve_modes(options = {})
-
10138
[options.delete(:mode) || options.delete(:modes) || current_mode_id].flatten.compact
-
end
-
-
# Returns an array containing the configuration ids resolved from the given options or
-
# the current top-level context
-
2
def resolve_configurations(options = {})
-
10138
[options.delete(:configuration) || options.delete(:configurations) || current_configuration].flatten.compact
-
end
-
-
2
def add_initial_packages(options)
-
8560
resolve_packages(options).each do |package|
-
52
if package.is_a?(Hash)
-
23
package.each do |id, attributes|
-
36
add_package(id, attributes)
-
end
-
else
-
29
add_package(package)
-
end
-
end
-
end
-
-
2
def add_initial_modes(options)
-
8560
resolve_modes(options).each do |mode|
-
2
if mode.is_a?(Hash)
-
mode.each do |id, attributes|
-
add_mode(id, attributes)
-
end
-
else
-
2
add_mode(mode)
-
end
-
end
-
end
-
-
2
def add_initial_configurations(options)
-
8560
resolve_configurations(options).each do |config|
-
if config.is_a?(Hash)
-
config.each do |id, attributes|
-
add_configuration(id, attributes)
-
end
-
else
-
add_configuration(config)
-
end
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Pins
-
2
class PowerPin < Pin
-
2
attr_accessor :current_limit
-
-
2
def initialize(id, owner, options = {}) # :nodoc:
-
83
v = options[:voltage] || options[:voltages]
-
83
self.voltage = v if v
-
83
self.current_limit = options[:current_limit] if options[:current_limit]
-
83
super
-
end
-
-
# Set the operating voltage for the pin, can be a single value or an array
-
2
def voltage=(val)
-
16
@voltages = [val].flatten.uniq
-
end
-
-
# Like voltages but if there is only one voltage known then it will be returned
-
# directly instead of being wrapped in an array.
-
# If no voltages are known this returns nil whereas voltages will return an
-
# empty array.
-
# For more than one voltages present this behaves like an alias of voltages.
-
2
def voltage
-
12
if voltages.size > 0
-
8
if voltages.size > 1
-
1
voltages
-
else
-
7
voltages.first
-
end
-
end
-
end
-
-
# Returns an array of known operating voltages for the given pin
-
2
def voltages
-
31
@voltages ||= []
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Pins
-
2
module Timing
-
2
class Timeset
-
2
attr_reader :id
-
-
# Returns an array containing the defined waves for drive cycles.
-
# The wave at position 0 will be applied be default to any pin which
-
# does not otherwise have a specific wave assignment.
-
2
attr_reader :drive_waves
-
-
# Returns an array containing the defined waves for compare cycles
-
# The wave at position 0 will be applied be default to any pin which
-
# does not otherwise have a specific wave assignment.
-
2
attr_reader :compare_waves
-
-
2
def initialize(id)
-
32
@id = id
-
32
@drive_waves = []
-
32
@compare_waves = []
-
# Look up tables that map pins to waves
-
32
@compare_pin_map = {}
-
32
@drive_pin_map = {}
-
# Temporary storage of pin assignments
-
32
@pin_ids = { drive: [], compare: [] }
-
-
# Create the default waves, these can be overridden later
-
32
wave do |w|
-
32
w.compare :data, at: 'period / 2'
-
end
-
-
32
wave do |w|
-
32
w.drive :data, at: 0
-
end
-
end
-
-
# Add a new drive or compare wave to the timeset
-
#
-
# timeset.wave :tck do |w|
-
# w.drive :data, at: 0
-
# w.drive 0, at: 25
-
# w.dont_care at: "period - 10"
-
# end
-
2
def wave(*pin_ids)
-
119
options = pin_ids.last.is_a?(Hash) ? pin_ids.pop : {}
-
119
w = Wave.new(self, options)
-
119
yield w
-
119
if w.drive?
-
69
if pin_ids.empty?
-
32
w.send(:index=, 0)
-
32
drive_waves[0] = w
-
32
@pin_ids[:drive][0] = pin_ids
-
else
-
37
w.send(:index=, drive_waves.size)
-
37
drive_waves << w
-
37
@pin_ids[:drive] << pin_ids
-
end
-
else
-
50
if pin_ids.empty?
-
41
w.send(:index=, 0)
-
41
compare_waves[0] = w
-
41
@pin_ids[:compare][0] = pin_ids
-
else
-
9
w.send(:index=, compare_waves.size)
-
9
compare_waves << w
-
9
@pin_ids[:compare] << pin_ids
-
end
-
end
-
end
-
2
alias_method :compare_wave, :wave
-
2
alias_method :drive_wave, :wave
-
-
# The timeset will cache a view of the dut's pins for performance,
-
# calling this method will clear that cache and regenerate the internal
-
# view. This should generally not be required, but available for corner cases
-
# where a pin is added to the dut after the cache has been generated.
-
2
def clear_cache
-
@all_pin_ids = nil
-
@groups = nil
-
compare_waves.each { |w| w.send(:clear_cache) }
-
drive_waves.each { |w| w.send(:clear_cache) }
-
end
-
-
2
private
-
-
# The pin assignments are done lazily to cater for the guy who will want
-
# to define waves ahead of pins or some such
-
2
def assign_pins
-
6
@pin_ids[:drive].each_with_index do |ids, i|
-
13
expand_groups(ids) do |id|
-
22
@drive_pin_map[id] ||= []
-
22
@drive_pin_map[id] << i
-
end
-
end
-
6
@pin_ids[:compare].each_with_index do |ids, i|
-
7
expand_groups(ids) do |id|
-
1
@compare_pin_map[id] ||= []
-
1
@compare_pin_map[id] << i
-
end
-
end
-
6
@pin_ids = :done
-
end
-
-
2
def expand_groups(ids)
-
20
ids.each do |id|
-
8
if g = dut.pin_groups[id]
-
1
g.each do |pin|
-
16
yield pin.id
-
end
-
else
-
7
yield id
-
end
-
end
-
end
-
-
2
def wave_for(pin, options)
-
38
assign_pins unless @pin_ids == :done
-
38
if options[:type] == :drive
-
20
i = @drive_pin_map[pin.id]
-
20
if i
-
19
if i.size > 1
-
3
code = options[:code]
-
3
code = nil if code == 1
-
3
code = nil if code == 0
-
3
i.each do |ix|
-
4
return drive_waves[ix] if drive_waves[ix].code == code
-
end
-
else
-
16
drive_waves[i[0]]
-
end
-
else
-
1
drive_waves[0]
-
end
-
else
-
18
i = @compare_pin_map[pin.id]
-
18
if i
-
1
if i.size > 1
-
code = options[:code]
-
code = nil if code == 'L' || code == :L
-
code = nil if code == 'H' || code == :H
-
i.each do |ix|
-
return drive_waves[ix] if drive_waves[ix].code == code
-
end
-
else
-
1
compare_waves[i[0]]
-
end
-
else
-
17
compare_waves[0]
-
end
-
end
-
end
-
-
2
def pin_ids_for(wave)
-
6
assign_pins unless @pin_ids == :done
-
6
map = wave.drive? ? @drive_pin_map : @compare_pin_map
-
6
if wave.index == 0
-
84
all_pin_ids.select { |id| !map[id] || map[id].include?(0) }
-
else
-
42
all_pin_ids.select { |id| map[id] && map[id].include?(wave.index) }
-
end
-
end
-
-
2
def all_pin_ids
-
6
@all_pin_ids ||= dut.pins.values.map(&:id)
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Pins
-
2
module Timing
-
2
class Wave
-
2
attr_reader :events, :timeset, :index
-
# Returns the pattern code value associated with the wave. By default this will return nil
-
# if no code was given at the time the wave was defined, which means it is the wave that will
-
# be applied for the conventional code values of 0, 1, H, L.
-
2
attr_reader :code
-
-
2
VALID_DRIVE_DATA = [0, 1, :data]
-
2
VALID_COMPARE_DATA = [0, 1, :data]
-
-
2
def initialize(timeset, options = {})
-
119
@code = options[:code]
-
119
@code = nil if [0, 1, 'H', 'L', :H, :L].include?(@code)
-
119
@timeset = timeset
-
119
@events = []
-
end
-
-
# Returns the events array but with any formula based times
-
# evaluated.
-
# Note that this does not raise an error if the period is not currently
-
# set, in that case any events that reference it will have nil for
-
# their time.
-
2
def evaluated_events
-
3
if dut.current_timeset_period
-
12
events.map { |e| [calc.evaluate(e[0], period: dut.current_timeset_period).ceil, e[1]] }
-
else
-
fail 'The current timeset period has not been set'
-
end
-
end
-
-
# Returns an array containing all dut pin_ids that
-
# are assigned to this wave by the parent timeset
-
2
def pin_ids
-
6
@pins_ids ||= timeset.send(:pin_ids_for, self)
-
end
-
-
# Returns an array containing all dut pin objects that
-
# are assigned to this wave by the parent timeset
-
2
def pins
-
89
@pins ||= pin_ids.map { |id| dut.pin(id) }
-
end
-
-
2
def drive(data, options)
-
110
self.type = :drive
-
110
validate_data(data) do |d|
-
110
validate_time(options) do |t|
-
110
events << [t, d]
-
end
-
end
-
end
-
-
2
def compare(data, options)
-
50
self.type = :compare
-
50
validate_data(data) do |d|
-
50
validate_time(options) do |t|
-
50
events << [t, d]
-
end
-
end
-
end
-
2
alias_method :compare_edge, :compare
-
-
2
def dont_care(options)
-
9
self.type = :drive
-
9
validate_time(options) do |t|
-
9
events << [t, :x]
-
end
-
end
-
2
alias_method :highz, :dont_care
-
-
2
def type
-
@type ||= :drive
-
end
-
-
2
def drive?
-
285
@type == :drive
-
end
-
-
2
def compare?
-
@type == :compare
-
end
-
-
2
private
-
-
2
def clear_cache
-
@pin_ids = nil
-
@pins = nil
-
end
-
-
2
def index=(val)
-
119
@index = val
-
end
-
-
2
def validate_data(data)
-
160
valid = drive? ? VALID_DRIVE_DATA : VALID_COMPARE_DATA
-
160
data = :data if :data == :pattern
-
160
unless valid.include?(data)
-
fail "Uknown data value #{data}, only these are valid: #{valid.join(', ')}"
-
end
-
160
yield data
-
end
-
-
2
def calc
-
129
return @calc if @calc
-
60
require 'dentaku'
-
60
@calc = Dentaku::Calculator.new
-
end
-
-
2
def type=(t)
-
169
if @type
-
50
if @type != t
-
fail 'Timing waves cannot both drive and compare within a cycle period!'
-
end
-
else
-
119
@type = t
-
end
-
end
-
-
2
def validate_time(options)
-
169
unless options[:at]
-
fail 'When defining a wave event you must supply the time via the option :at'
-
end
-
169
t = options[:at]
-
-
169
if t.is_a?(String)
-
60
d = calc.dependencies(t) - %w(period period_in_ns)
-
60
unless d.empty?
-
fail "Wave time formulas can only include the variable 'period' (or 'period_in_ns'), this variable is not allowed: #{d}"
-
end
-
60
t = t.gsub('period_in_ns', 'period')
-
60
unless calc.evaluate(t, period: 100)
-
fail "There appears to be an error in the formula: #{t}"
-
end
-
60
yield t
-
60
return
-
109
elsif t.is_a?(Numeric)
-
109
yield t
-
109
return
-
end
-
fail 'The :at option in a wave event definition must be either a number or a string'
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Pins
-
2
class VirtualPin < Pin
-
2
def type=(value)
-
2
@type = value
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Ports
-
2
class BitCollection < Registers::BitCollection
-
end
-
end
-
end
-
2
module Origen
-
2
module Ports
-
2
class Port
-
2
include Netlist::Connectable
-
-
2
attr_reader :size
-
2
attr_reader :parent
-
2
attr_reader :id
-
2
attr_reader :type
-
-
2
alias_method :name, :id
-
2
alias_method :owner, :parent
-
-
2
def initialize(parent, id, options = {})
-
80
@size = options[:size] || 1
-
80
@parent = parent
-
80
@id = id
-
80
@type = options[:type]
-
80
@bit_names = {}.with_indifferent_access
-
end
-
-
2
def inspect
-
1
"<#{self.class}:#{object_id} id:#{id} path:#{path}>"
-
end
-
-
2
def describe(options = {})
-
2
desc = ['********************']
-
2
desc << "Port id: #{id}"
-
2
desc << "Port path: #{path}"
-
2
desc << ''
-
2
desc << 'Connections'
-
2
desc << '-----------'
-
2
desc << ''
-
2
table = netlist.table
-
2
((size - 1)..0).to_a.each do |i|
-
16
if table[path]
-
c = [table[path]['*'], table[path][i]].flatten.compact.map { |n| n.is_a?(Proc) ? 'Proc' : n }
-
desc << "#{i} - #{c.shift}"
-
c.each do |n|
-
desc << " - #{n}"
-
end
-
else
-
16
desc << "#{i} - none"
-
end
-
end
-
2
desc << ''
-
-
2
if options[:return]
-
1
desc
-
else
-
1
puts desc.join("\n")
-
end
-
end
-
-
2
def path
-
517
if parent.path.empty?
-
159
id.to_s
-
else
-
358
"#{parent.path}.#{id}"
-
end
-
end
-
-
2
def bits(index, name, options = {})
-
2
if @defining
-
2
@bit_names[name] = index
-
else
-
fail 'Cannot add additional port bits once the port definition is complete'
-
end
-
end
-
-
# Prevent infinite loop if a child bit collection checks bit_order
-
2
def bit_order
-
parent.bit_order
-
end
-
-
2
def drive(value = nil, options = {})
-
25
value, options = nil, value if value.is_a?(Hash)
-
25
if options[:index]
-
2
if options[:index].is_a?(Integer)
-
2
drive_values[options[:index]] = value ? value[0] : nil
-
else
-
options[:index].to_a.each do |i|
-
drive_values[i] = value ? value[i] : nil
-
end
-
end
-
else
-
23
size.times do |i|
-
65
drive_values[i] = value ? value[i] : nil
-
end
-
end
-
25
@drive_value = value
-
end
-
-
2
def drive_values
-
1004
@drive_values ||= Array.new(size)
-
end
-
-
2
def to_section
-
171
Section.new(self, (size - 1)..0)
-
end
-
-
2
def method_missing(method, *args, &block)
-
174
if @bit_names.key?(method)
-
3
Section.new(self, @bit_names[method])
-
171
elsif BitCollection.instance_methods.include?(method)
-
171
to_bc.send(method, *args, &block)
-
else
-
super
-
end
-
end
-
-
2
def respond_to?(*args)
-
228
@bit_names.key?(args.first) || super(*args) ||
-
BitCollection.instance_methods.include?(args.first)
-
end
-
-
2
def [](val)
-
772
Section.new(self, val)
-
end
-
-
2
def to_bc
-
171
to_section.to_bc
-
end
-
-
2
private
-
-
2
def defining
-
2
@defining = true
-
2
yield
-
2
@defining = false
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Ports
-
2
class PortCollection < ::Hash
-
2
def add(name, port)
-
80
self[name] = port
-
80
by_type[port.type] ||= []
-
80
by_type[port.type] << port
-
end
-
-
2
def by_type
-
161
@by_type ||= {}.with_indifferent_access
-
end
-
-
2
def inspect
-
map { |k, _v| k }.inspect
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Ports
-
2
class Section
-
2
include Netlist::Connectable
-
-
2
attr_reader :port
-
2
attr_reader :index
-
-
2
def initialize(port, index)
-
949
@port = port
-
949
@index = index
-
end
-
-
2
def size
-
939
size_of(index)
-
end
-
-
2
def path
-
63
if index.is_a?(Range)
-
5
port.path + "[#{index.first}:#{index.last}]"
-
else
-
58
port.path + "[#{index}]"
-
end
-
end
-
-
2
def parent
-
364
port.parent
-
end
-
2
alias_method :owner, :parent
-
-
2
def id
-
port.id
-
end
-
-
2
def drive(value = nil)
-
2
port.drive(value, index: index)
-
end
-
-
2
def drive_value
-
937
if size == 1
-
937
port.drive_values[index]
-
else
-
fail 'drive_value is only supported for a single bit port section'
-
end
-
end
-
-
2
def [](index)
-
3
Section.new(port, align_to_port(index))
-
end
-
-
2
def respond_to?(*args)
-
182
super(*args) || BitCollection.instance_methods.include?(args.first)
-
end
-
-
2
def method_missing(method, *args, &block)
-
9
if BitCollection.instance_methods.include?(method)
-
9
to_bc.send(method, *args, &block)
-
else
-
super
-
end
-
end
-
-
2
def to_bc
-
180
b = BitCollection.new(port, port.id)
-
180
indexes = index.respond_to?(:to_a) ? index.to_a : [index]
-
180
indexes.reverse_each do |i|
-
385
b << netlist.data_bit(port.path, i)
-
end
-
180
b
-
end
-
-
2
private
-
-
2
def size_of(index)
-
941
if index.is_a?(Range)
-
3
(index.first - index.last).abs + 1
-
else
-
938
1
-
end
-
end
-
-
2
def align_to_port(val)
-
3
if val.is_a?(Range)
-
2
nlsb = lsb + val.last
-
2
nmsb = nlsb + size_of(val) - 1
-
2
out_of_range(val) if nmsb > msb
-
2
i = nmsb..nlsb
-
else
-
1
i = lsb + val
-
1
out_of_range(val) if i > msb
-
end
-
3
i
-
end
-
-
2
def out_of_range(val)
-
fail "Requested section index (#{val}) is out of range for a port section of size #{size}"
-
end
-
-
2
def msb
-
3
if index.is_a?(Range)
-
3
index.first
-
else
-
index
-
end
-
end
-
-
2
def lsb
-
3
if index.is_a?(Range)
-
3
index.last
-
else
-
index
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Registers
-
# Models bits within Reg objects
-
2
class Bit
-
# The :access property of registers or bits can be set to any of the following
-
# key values. Implemented refers to whether the behaviour is accurately modelled
-
# by the Origen register model or not.
-
#
-
# :base is used in CrossOrigen to set the IP-XACT access type on export.
-
#
-
# :read and :write are used in CrossOrigen for IP-XACT export to cover 'readAction'
-
# and 'modifiedWriteValue' attributes in the IEEE 1685-2009 schema - they do not affect
-
# Origen Core functionality (yet?).
-
ACCESS_CODES = {
-
2
ro: { implemented: false, base: 'read-only', write: nil, read: nil, writable: false, readable: true, w1c: false, set_only: false, clr_only: false, description: 'Read-Only' },
-
rw: { implemented: true, base: 'read-write', write: nil, read: nil, writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: 'Read-Write' },
-
rc: { implemented: false, base: 'read-only', write: nil, read: 'clear', writable: false, readable: true, w1c: false, set_only: false, clr_only: false, description: 'Read-only, Clear-on-read' },
-
rs: { implemented: false, base: 'read-only', write: nil, read: 'set', writable: false, readable: true, w1c: false, set_only: false, clr_only: false, description: "Set-on-read (all bits become '1' on read)" },
-
wrc: { implemented: false, base: 'read-write', write: nil, read: 'clear', writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: 'Writable, clear-on-read' },
-
wrs: { implemented: false, base: 'read-write', write: nil, read: 'set', writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: 'Writable, Sets-on-read' },
-
wc: { implemented: false, base: 'read-write', write: 'clear', read: nil, writable: true, readable: true, w1c: false, set_only: false, clr_only: true, description: 'Clear-on-write' },
-
ws: { implemented: false, base: 'read-write', write: 'set', read: nil, writable: true, readable: true, w1c: false, set_only: true, clr_only: false, description: 'Set-on-write' },
-
wsrc: { implemented: false, base: 'read-write', write: 'set', read: 'clear', writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: 'Set-on-write, clear-on-read' },
-
wcrs: { implemented: false, base: 'read-write', write: 'clear', read: 'set', writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: 'Clear-on-write, set-on-read' },
-
w1c: { implemented: false, base: 'read-write', write: 'oneToClear', read: nil, writable: true, readable: true, w1c: true, set_only: false, clr_only: false, description: "Write '1' to clear bits" },
-
w1s: { implemented: false, base: 'read-write', write: 'oneToSet', read: nil, writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: "Write '1' to set bits" },
-
w1t: { implemented: false, base: 'read-write', write: 'oneToToggle', read: nil, writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: "Write '1' to toggle bits" },
-
w0c: { implemented: false, base: 'read-write', write: 'zeroToClear', read: nil, writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: "Write '0' to clear bits" },
-
w0s: { implemented: false, base: 'read-write', write: 'zeroToSet', read: nil, writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: "Write '0' to set bits" },
-
w0t: { implemented: false, base: 'read-write', write: 'zeroToToggle', read: nil, writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: "Write '0' to toggle bits" },
-
w1src: { implemented: false, base: 'read-write', write: 'oneToSet', read: 'clear', writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: "Write '1' to set and clear-on-read" },
-
w1crs: { implemented: false, base: 'read-write', write: 'oneToClear', read: 'set', writable: true, readable: true, w1c: true, set_only: false, clr_only: false, description: "Write '1' to clear and set-on-read" },
-
w0src: { implemented: false, base: 'read-write', write: 'zeroToSet', read: 'clear', writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: "Write '0' to set and clear-on-read" },
-
w0crs: { implemented: false, base: 'read-write', write: 'zeroToClear', read: 'set', writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: "Write '0' to clear and set-on-read" },
-
wo: { implemented: false, base: 'write-only', write: nil, read: nil, writable: true, readable: false, w1c: false, set_only: false, clr_only: false, description: 'Write-only' },
-
woc: { implemented: false, base: 'write-only', write: 'clear', read: nil, writable: true, readable: false, w1c: false, set_only: false, clr_only: true, description: "When written sets the field to '0'. Read undeterministic" },
-
worz: { implemented: false, base: 'write-only', write: nil, read: nil, writable: true, readable: false, w1c: false, set_only: false, clr_only: false, description: 'Write-only, Reads zero' },
-
wos: { implemented: false, base: 'write-only', write: 'set', read: nil, writable: true, readable: false, w1c: false, set_only: true, clr_only: false, description: "When written sets all bits to '1'. Read undeterministic" },
-
w1: { implemented: false, base: 'read-writeOnce', write: nil, read: nil, writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: 'Write-once. Next time onwards, write is ignored. Read returns the value' },
-
wo1: { implemented: false, base: 'writeOnce', write: nil, read: nil, writable: true, readable: false, w1c: false, set_only: false, clr_only: false, description: 'Write-once. Next time onwards, write is ignored. Read is undeterministic' },
-
dc: { implemented: false, base: 'read-write', write: nil, read: nil, writable: true, readable: true, w1c: false, set_only: false, clr_only: false, description: 'RW but no check' },
-
rowz: { implemented: false, base: 'read-only', write: nil, read: 'clear', writable: false, readable: true, w1c: false, set_only: false, clr_only: false, description: 'Read-only, value is cleared on read' }
-
}
-
-
# Returns the Reg object that owns the bit
-
2
attr_reader :owner
-
# Returns the integer position of the bit within the register
-
2
attr_reader :position
-
# Current the data value currently held by the bit, 0 or 1
-
2
attr_reader :data
-
# Returns any overlay string attached to the bit
-
2
attr_reader :overlay
-
# If the bit does not read back with the same data as is written to it
-
# then this will return true. This property can be assigned durgin the
-
# register instantiation, e.g.
-
# add_reg :control, 0x00, :mode => { :pos => 8, :bits => 8 },
-
# :status => { :pos => 4, :bits => 2, :read_data_matches_write => false }
-
2
attr_reader :read_data_matches_write
-
# Returns true if this bit has the sticky_overlay flag set, see Reg#sticky_overlay for
-
# a full description. This is true by default.
-
2
attr_accessor :sticky_overlay
-
# Returns true if this bit has the sticky_store flag set, see Reg#sticky_store for
-
# a full description. This is false by default.
-
2
attr_accessor :sticky_store
-
# Any feature associated with the bit/bits
-
2
attr_reader :feature
-
# Returns the reset value of the bit
-
2
attr_accessor :reset_val
-
2
alias_method :reset_data, :reset_val
-
2
alias_method :reset_value, :reset_val
-
# Allow modify of writable flag, bit is writeable by write method
-
2
attr_writer :writable
-
# Allow modify of readable flag, bit is readable by read method
-
2
attr_writer :readable
-
# Sets or returns the status of "write-one-to-clear"
-
2
attr_accessor :w1c
-
# Allow modify of clr_only flag, bit can only be cleared (made 0)
-
2
attr_writer :clr_only
-
# Allow modify of set_only flag, bit can only be set (made 1)
-
2
attr_writer :set_only
-
# Returns read_action - whether anything happens to the bit when read
-
2
attr_reader :read_action
-
# Returns mod_write_value - what write value modification occurs when written
-
2
attr_reader :mod_write_value
-
# Returns true if bit depends on initial state of NVM in some way
-
2
attr_reader :nvm_dep
-
# Returns true if bit is critical to starting an important operation (like a state machine)
-
# so that it can be made not writable during basic register checks
-
2
attr_reader :start
-
# Returns any application-specific meta-data attatched to the given bit
-
2
attr_accessor :meta
-
2
alias_method :meta_data, :meta
-
2
alias_method :metadata, :meta
-
# Returns the access method for the given bit (a symbol), see the ACCESS_CODES constant for
-
# the possible values this can have and their meaning
-
2
attr_accessor :access
-
# Returns the basic access string for a given access method. Possible values: read-write, read-only,
-
# write-only, writeOnce, read-writeOnce. Used primarily by CrossOrigen IP-XACT import/export.
-
2
attr_reader :base_access
-
# Can be set to indicate that the current state of the bit is unknown, e.g. after reading X from a simulation
-
2
attr_accessor :unknown
-
-
2
def initialize(owner, position, options = {}) # rubocop:disable MethodLength
-
options = {
-
16173
start: false, # whether bit starts a state machine so be careful
-
read_data_matches_write: true,
-
read: false,
-
overlay: false,
-
store: false,
-
sticky_overlay: true,
-
sticky_store: false,
-
nvm_dep: false, # whether is an NVM dependent bit
-
}.merge(options)
-
16173
@owner = owner
-
16173
@position = position
-
16173
@undefined = options.delete(:undefined)
-
16173
@reset_val = (options.delete(:res) || options.delete(:reset) || options.delete(:data) || 0)
-
16173
if @reset_val.is_a?(Symbol)
-
26
@data = 0
-
else
-
16147
@reset_val &= 1 unless @reset_val.is_a?(Symbol)
-
16147
@data = @reset_val
-
end
-
-
16173
access_code = options.delete(:access)
-
# If access has been defined then none of these other attributes can be
-
16173
if access_code
-
431
conflicts = [:readable, :writable, :clr_only, :set_only, :w1c]
-
2586
if conflicts.any? { |k| options.key?(k) }
-
puts 'The following attributes cannot be set in combination with :access'
-
puts " #{conflicts.join(', ')}"
-
puts ''
-
puts 'Use :access to defined the required behavior, the above attributes will be deprecated in future.'
-
puts ''
-
fail 'Conflicting access!'
-
end
-
431
set_access(access_code)
-
else
-
options = {
-
15742
writable: true, # whether bit is writable
-
readable: true, # whether bit is readable
-
clr_only: false, # whether bit is clear only
-
set_only: false, # whether bit is set only
-
w1c: false, # whether bit is w1c (when written to 1 immediately becomes 0)
-
}.merge(options)
-
15742
@readable = options.delete(:readable)
-
15742
@writable = options.delete(:writable)
-
15742
@clr_only = options.delete(:clr_only)
-
15742
@set_only = options.delete(:set_only)
-
15742
@w1c = options.delete(:w1c)
-
15742
set_access_from_rw
-
end
-
# Would like to get this integrated with access as well
-
16173
@read_data_matches_write = options.delete(:read_data_matches_write)
-
-
16173
@feature = options.delete(:feature)
-
16173
if !!feature && @writable
-
102
@writable = enabled?
-
end
-
16173
@path = options.delete(:path)
-
16173
@abs_path = options.delete(:abs_path)
-
16173
@start = options.delete(:start)
-
16173
@read = options.delete(:read)
-
16173
@overlay = options.delete(:overlay)
-
16173
@store = options.delete(:store)
-
16173
@update_required = false
-
16173
@sticky_store = options.delete(:sticky_store)
-
16173
@sticky_overlay = options.delete(:sticky_overlay)
-
16173
@nvm_dep = (options.delete(:nvm_dep) ? 1 : 0)
-
# Delete some other noise that can be left over...
-
16173
options.delete(:bits)
-
16173
options.delete(:pos)
-
16173
options.delete(:position)
-
16173
options.delete(:data)
-
# Whatever is left must be custom application meta-data
-
16173
@meta = (default_bit_metadata).merge(options)
-
end
-
-
2
def access_codes
-
ACCESS_CODES
-
end
-
-
2
def set_access(value)
-
431
unless ACCESS_CODES.keys.include?(value)
-
puts 'Invalid access code, must be one of these:'
-
ACCESS_CODES.each do |code, meta|
-
puts " :#{code}".ljust(10) + " - #{meta[:description]}"
-
end
-
puts ''
-
fail 'Invalid access code!'
-
end
-
431
@access = value
-
-
# Set access attributes by pulling key-value pairs from ACCESS_CODES[<access>]
-
431
@readable = ACCESS_CODES[@access][:readable]
-
431
@writable = ACCESS_CODES[@access][:writable]
-
431
@w1c = ACCESS_CODES[@access][:w1c]
-
431
@set_only = ACCESS_CODES[@access][:set_only]
-
431
@clr_only = ACCESS_CODES[@access][:clr_only]
-
431
@base_access = ACCESS_CODES[@access][:base]
-
431
@read_action = ACCESS_CODES[@access][:read]
-
431
@mod_write_value = ACCESS_CODES[@access][:write]
-
end
-
-
# Set @access based on @readable and @writable
-
2
def set_access_from_rw
-
15742
if @w1c
-
1
@access = :w1c
-
15741
elsif @clr_only
-
@access = :wc
-
15741
elsif @set_only
-
@access = :ws
-
15741
elsif @readable && @writable
-
8299
@access = :rw
-
7442
elsif @readable
-
7442
@access = :ro
-
elsif @writable && @access != :worz
-
@access = :wo
-
end
-
end
-
-
2
def path_var
-
44
@path
-
end
-
-
2
def abs_path
-
38
@abs_path
-
end
-
-
2
ACCESS_CODES.each do |code, _meta|
-
56
define_method "#{code}?" do
-
13
!!(access == code || instance_variable_get("@#{code}"))
-
end
-
end
-
-
# Returns any application specific metadata that has been inherited by the
-
# given bit.
-
# This does not account for any overridding that may have been applied to
-
# this bit specifically however, use the meta method to get that.
-
2
def default_bit_metadata
-
39868
if owner
-
39573
Origen::Registers.default_bit_metadata.merge(
-
Origen::Registers.bit_metadata[owner.owner.class] || {})
-
else
-
295
Origen::Registers.default_bit_metadata
-
end
-
end
-
-
2
def inspect
-
"<#{self.class}:#{object_id}>"
-
end
-
-
# Always returns 1 when asked for size, a BitCollection on the other hand will return something higher
-
2
def size
-
1
-
end
-
-
# Make this bit disappear, make it unwritable with a data value of 0
-
2
def delete
-
@sticky_overlay = false
-
@sticky_store = false
-
clear_flags
-
@data = 0
-
@writable = false
-
self
-
end
-
-
# Returns true if the bit is set (holds a data value of 1)
-
2
def set?
-
2215
@data == 1 ? true : false
-
end
-
-
# Resets the data value back to the reset value and calls Bit#clear_flags
-
2
def reset
-
2083
if @reset_val.is_a?(Symbol)
-
2
@data = 0
-
else
-
2081
@data = @reset_val
-
end
-
2083
@updated_post_reset = false
-
2083
clear_flags
-
2083
self
-
end
-
-
# Returns true if the bit object is a placeholder for bit positions that have
-
# not been defined within the parent register
-
2
def undefined?
-
36
@undefined
-
end
-
-
# Returns true if the value of the bit is known. The value will be
-
# unknown in cases where the reset value is undefined or determined by a memory location
-
# and where the bit has not been written or read to a specific value yet.
-
2
def has_known_value?
-
410
!@unknown && (!@reset_val.is_a?(Symbol) || @updated_post_reset)
-
end
-
-
# Set the data value of the bit to the given value (1 or 0)
-
# If the bit is read-only, the value of the bit can be forced with 'force: true'
-
2
def write(value, options = {})
-
# If an array is written it means a data value and an overlay have been supplied
-
# in one go...
-
7921
if value.is_a?(Array)
-
overlay(value[1])
-
value = value[0]
-
end
-
7921
if (@data != value & 1 && @writable) ||
-
(@data != value & 1 && options[:force] == true)
-
1258
if ((set?) && (!@set_only)) ||
-
957
((!set?) && (!@clr_only))
-
1256
@data = value & 1
-
1256
@update_required = true
-
1256
@updated_post_reset = true
-
end
-
end
-
7921
self
-
end
-
-
# Will tag all bits for read and if a data value is supplied it
-
# will update the expected data for when the read is performed.
-
2
def read(value = nil, _options = {})
-
# First properly assign the args if value is absent...
-
1411
if value.is_a?(Hash)
-
options = value
-
value = nil
-
end
-
1411
write(value) if value
-
1411
@read = true if @readable && @read_data_matches_write
-
1411
self
-
end
-
2
alias_method :assert, :read
-
-
# Sets the store flag attribute
-
2
def store
-
10
@store = true
-
10
self
-
end
-
-
# Set the overlay attribute to the supplied value
-
2
def overlay(value)
-
127
@overlay = value
-
127
self
-
end
-
-
# Returns the overlay attribute
-
2
def overlay_str
-
96
@overlay
-
end
-
-
# Returns true if the bit's read flag is set
-
2
def is_to_be_read?
-
5122
@read
-
end
-
-
# Returns true if the bit's store flag is set
-
2
def is_to_be_stored?
-
5059
@store
-
end
-
-
# Returns true if the overlay attribute is set, optionally supply an overlay
-
# name and this will only return true if the overlay attribute matches that name
-
2
def has_overlay?(name = nil)
-
4368
if name
-
name.to_s == @overlay.to_s
-
else
-
4368
!!@overlay
-
end
-
end
-
-
# Returns true if the bit is writable
-
2
def is_writable?
-
69
@writable
-
end
-
2
alias_method :writable?, :is_writable?
-
-
2
def is_readable?
-
69
@readable
-
end
-
2
alias_method :readable?, :is_readable?
-
-
# Clears the read, store, overlay and update_required flags of this bit.
-
# The store and overlay flags will not be cleared if the the bit's sticky_store
-
# or sticky_overlay attributes are set respectively.
-
2
def clear_flags
-
5303
@read = false
-
5303
@store = false unless @sticky_store
-
5303
@overlay = false unless @sticky_overlay
-
5303
@update_required = false
-
5303
self
-
end
-
-
# Clears the read flag of this bit.
-
2
def clear_read_flag
-
33
@read = false
-
33
self
-
end
-
-
# Returns a bit mask for this bit, that is a 1 shifted into the position
-
# corresponding to this bit's position. e.g. A bit with position 4 would return
-
# %1_0000
-
2
def mask
-
mask_val = 1
-
mask_val << @position
-
end
-
-
# Returns a 'null' bit object which has value 0 and no other attributes set
-
2
def self.null(owner, position) # :nodoc:
-
Bit.new(owner, position, writable: false)
-
end
-
-
# Returns the value you would need to write to the register to put the given
-
# value in this bit
-
2
def setting(value)
-
39
value = value & 1 # As this bit can only hold one bit of data force it
-
39
value << @position
-
end
-
-
# Returns true if the bit's update_required flag is set, typically this will be the
-
# case when a write has changed the data value of the bit but a BitCollection#write!
-
# method has not been called yet to apply it to silicon
-
2
def update_required?
-
30
@update_required
-
end
-
-
# With only one bit it just returns itself
-
2
def shift_out_left
-
yield self
-
end
-
-
# Returns the data shifted by the bit position
-
2
def data_in_position
-
2
data << position
-
end
-
-
# Clears any w1c bits that are set
-
2
def clear_w1c
-
if @w1c && set?
-
@data = 0
-
end
-
self
-
end
-
-
# Clears any start bits that are set
-
2
def clear_start
-
if @start && set?
-
@data = 0
-
end
-
self
-
end
-
-
2
def respond_to?(*args) # :nodoc:
-
23666
sym = args.first
-
23666
meta_data_method?(sym) || super(sym)
-
end
-
-
# @api private
-
2
def meta_data_method?(method)
-
23695
attr_name = method.to_s.gsub(/\??=?/, '').to_sym
-
23695
if default_bit_metadata.key?(attr_name)
-
61
if method.to_s =~ /\?/
-
[true, false].include?(default_bit_metadata[attr_name])
-
else
-
61
true
-
end
-
else
-
23634
false
-
end
-
end
-
-
2
def extract_meta_data(method, *args)
-
20
method = method.to_s.sub('?', '')
-
20
if method =~ /=/
-
2
instance_variable_set("@#{method.sub('=', '')}", args.first)
-
else
-
18
instance_variable_get("@#{method}") || meta[method.to_sym]
-
end
-
end
-
-
2
def method_missing(method, *args, &block) # :nodoc:
-
20
if meta_data_method?(method)
-
20
extract_meta_data(method, *args)
-
else
-
super
-
end
-
end
-
-
# Returns true if the bit is constrained by the given/any feature
-
2
def enabled_by_feature?(name = nil)
-
406
if !name
-
212
!!feature
-
else
-
194
if feature.class == Array
-
1
feature.each do |f|
-
2
if f == name
-
return true
-
end
-
end
-
1
return false
-
else
-
193
feature == name
-
end
-
end
-
end
-
2
alias_method :has_feature_constraint?, :enabled_by_feature?
-
-
2
def enabled?
-
26904
if feature
-
224
value = false
-
224
current_owner = self
-
224
if feature.class == Array
-
1
feature.each do |f|
-
2
current_owner = self
-
2
loop do
-
4
if current_owner.respond_to?(:owner)
-
4
current_owner = current_owner.owner
-
4
if current_owner.respond_to?(:has_feature?)
-
2
if current_owner.has_feature?(f)
-
2
value = true
-
2
break
-
end
-
end
-
else # if current owner does not have a owner
-
value = false
-
break
-
end
-
end # loop end
-
2
unless value
-
if Origen.top_level && \
-
Origen.top_level.respond_to?(:has_feature?) && \
-
Origen.top_level.has_feature?(f)
-
value = true
-
unless value
-
break
-
end
-
end
-
end
-
2
unless value
-
break # break if feature not found and return false
-
end
-
end # iterated through all features in array
-
1
return value
-
else # if feature.class != Array
-
223
loop do
-
555
if current_owner.respond_to?(:owner)
-
502
current_owner = current_owner.owner
-
502
if current_owner.respond_to?(:has_feature?)
-
226
if current_owner.has_feature?(feature)
-
170
value = true
-
170
break
-
end
-
end
-
else # if current owner does not have a owner
-
53
value = false
-
53
break
-
end
-
end # loop end
-
223
unless value
-
53
if Origen.top_level && \
-
Origen.top_level.respond_to?(:has_feature?) && \
-
Origen.top_level.has_feature?(feature)
-
value = true
-
end
-
end
-
223
return value
-
end
-
else
-
26680
return true
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Registers
-
# This is a regular Ruby array that is used to store collections of Bit objects, it has additional
-
# methods added to allow interaction with the contained bits.
-
# All Ruby array methods are also available - https://www.ruby-doc.org/core/classes/Array.html
-
#
-
# A BitCollection is returned whenever a subset of bits is requested from a register. Also whenever
-
# any of these methods are called on a register object a BitCollection is created on the fly that
-
# contains all bits in the register. This means that when interacting with a Register, a single Bit,
-
# or a group of Bit objects, the same API can be used as described below.
-
2
class BitCollection < Array
-
2
include Origen::SubBlocks::Path
-
2
include Netlist::Connectable
-
-
2
DONT_CARE_CHAR = 'X'
-
2
OVERLAY_CHAR = 'V'
-
2
STORE_CHAR = 'S'
-
2
UNKNOWN_CHAR = '?'
-
-
2
attr_accessor :name
-
2
alias_method :id, :name
-
-
2
def initialize(reg, name, data = [], options = {}) # :nodoc:
-
26814
if reg.respond_to?(:has_bits_enabled_by_feature?) && reg.has_parameter_bound_bits?
-
170
reg.update_bound_bits unless reg.updating_bound_bits?
-
end
-
26814
@reg = reg
-
26814
@name = name
-
26814
@with_bit_order = options[:with_bit_order] || :lsb0
-
47119
[data].flatten.each { |item| self << item }
-
end
-
-
# Returns the bit order of the parent register
-
2
def bit_order
-
2
parent.bit_order
-
end
-
-
# Returns the bit numbering order to use when interpreting indeces
-
2
def with_bit_order
-
6
@with_bit_order
-
end
-
-
# Allow bit number interpreting to be explicitly set to msb0
-
2
def with_msb0
-
28
@with_bit_order = :msb0
-
28
self
-
end
-
-
# Allow bit number interpreting to be explicitly set to lsb0
-
2
def with_lsb0
-
17009
if block_given?
-
# run just the code block with lsb0 numbering (for internal methods)
-
378
saved_wbo = @with_bit_order
-
378
@with_bit_order = :lsb0
-
378
yield
-
378
@with_bit_order = saved_wbo
-
else
-
16631
@with_bit_order = :lsb0
-
16631
self
-
end
-
end
-
-
2
def terminal?
-
true
-
end
-
-
2
def bind(live_parameter)
-
2
parent.bind(name, live_parameter)
-
end
-
-
# Access bits by index
-
#
-
# **Note** This method behaves differently depending on the setting of @with_bit_order
-
#
-
# If @with_bit_order == :lsb0 (default) index 0 refers to the lsb of the bit collection
-
# If @with_bit_order == :msb0 index 0 refers to the msb of the bit collection
-
#
-
# ==== Example
-
# dut.reg(:some_reg).bits(:some_field).with_msb0[0..1] # returns 2 most significant bits
-
# dut.reg(:some_reg).bits(:some_field)[0..1] # returns 2 least significant bits
-
#
-
# **Note** Internal methods should call this method using a with_lsb0 block around the code
-
# or alternatively use the shift_out methods
-
# ==== Example
-
# with_lsb0 do
-
# saved_bit = [index]
-
# [index] = some_new_bit_or_operation
-
# end
-
2
def [](*indexes)
-
8339
return self if indexes.empty?
-
8338
b = BitCollection.new(parent, name)
-
8338
expand_and_order(*indexes).each do |i|
-
8429
b << fetch(i)
-
end
-
# When 1 bit requested just return that bit, this is consistent with the original
-
# behaviour before sub collections were added
-
8338
if b.size == 1
-
8318
b.first
-
else
-
# maintain downstream bit numbering setting
-
20
@with_bit_order == :msb0 ? b.with_msb0 : b
-
end
-
end
-
2
alias_method :bits, :[]
-
2
alias_method :bit, :[]
-
-
2
def parent
-
8476
@reg
-
end
-
-
2
def path_var
-
28
if first.path_var
-
8
if first.path_var =~ /^\./
-
4
base = parent.path(relative_to: parent.parent)
-
4
"#{base}#{first.path_var}"
-
else
-
4
first.path_var
-
end
-
else
-
20
base = parent.path(relative_to: parent.parent)
-
20
if size == 1
-
10
"#{base}[#{position}]"
-
else
-
10
"#{base}[#{position + size - 1}:#{position}]"
-
end
-
end
-
end
-
-
2
def abs_path
-
38
first.abs_path
-
end
-
-
2
Bit::ACCESS_CODES.each do |code, _meta|
-
56
define_method "#{code}?" do
-
40
all? { |b| b.undefined? || b.send("#{code}?") }
-
end
-
end
-
-
# Update the register contents with the live value from the device under test.
-
#
-
# The current tester needs to be an OrigenLink driver. Upon calling this method a request will
-
# be made to read the given register, the read data will be captured and the register model
-
# will be updated.
-
#
-
# The register parent register object is returned, this means that calling .sync on a register
-
# or bitcollection object will automatically update it and the display the register in the
-
# console.
-
#
-
# Normally this method should be called from a breakpoint during pattern debug, and it is
-
# not intended to be inserted into production pattern logic.
-
2
def sync(size = nil, options = {})
-
size, options = nil, size if size.is_a?(Hash)
-
if tester.respond_to?(:capture)
-
preserve_flags do
-
v = tester.capture do
-
store!(sync: true)
-
end
-
if v.first
-
# Serial shift
-
if v.size == 1
-
reverse_shift_out_with_index do |bit, i|
-
bit.instance_variable_set('@updated_post_reset', true)
-
bit.instance_variable_set('@data', v.first[i])
-
end
-
# Parallel shift
-
else
-
reverse_shift_out_with_index do |bit, i|
-
bit.instance_variable_set('@updated_post_reset', true)
-
bit.instance_variable_set('@data', v[i].to_i)
-
end
-
end
-
else
-
Origen.log.warning "No data was captured when attempting to sync register #{owner.name}, this is probably because the current read_register driver method does not implement store requests"
-
end
-
end
-
if size
-
puts "#{parent.address.to_s(16).upcase}: " + data.to_s(16).upcase.rjust(Origen.top_level.memory_width / 4, '0')
-
if size > 1
-
step = Origen.top_level.memory_width / 8
-
Origen.top_level.mem(parent.address + step).sync(size - 1)
-
end
-
nil
-
else
-
parent
-
end
-
else
-
Origen.log.warning 'Sync is not supported on the current tester driver, register not updated'
-
end
-
end
-
2
alias_method :sync!, :sync
-
-
# At the end of the given block, the status flags of all bits will be restored to the state that
-
# they were upon entry to the block
-
2
def preserve_flags
-
orig = []
-
each do |bit|
-
orig << [bit.overlay_str, bit.is_to_be_read?, bit.is_to_be_stored?]
-
end
-
yield
-
each do |bit|
-
bit.clear_flags
-
flags = orig.shift
-
bit.overlay(flags[0])
-
bit.read if flags[1]
-
bit.store if flags[2]
-
end
-
self
-
end
-
-
# Copies all data and flags from one bit collection (or reg) object to another
-
#
-
# This method will accept a dumb value as the argument, in which case it is essentially a write,
-
# however it will also clear all flags.
-
2
def copy_all(reg)
-
26
if reg.respond_to?(:contains_bits?) && reg.contains_bits?
-
25
unless reg.size == size
-
puts 'Bit collection copy must be performed on collections of the same size.'
-
puts 'You can fix this by calling copy on a subset of the bits you require, e.g.'
-
puts ' larger_bit_collection[3..0].copy_all(smaller_bit_collection)'
-
puts
-
fail 'Mismatched size for bit collection copy'
-
end
-
# safely handle collections with differing with_bit_order settings
-
25
with_lsb0 do
-
25
reg.shift_out_with_index do |source_bit, i|
-
760
if source_bit
-
760
self[i].overlay(source_bit.overlay_str) if source_bit.has_overlay?
-
760
self[i].write(source_bit.data)
-
-
760
self[i].read if source_bit.is_to_be_read?
-
760
self[i].store if source_bit.is_to_be_stored?
-
end
-
end
-
end # of with_lsb0
-
else
-
1
write(reg)
-
1
clear_flags
-
end
-
26
self
-
end
-
-
# Returns the access attribute of the first contained bit, in most normal use cases
-
# the application will naturally guarantee that when this is called all of the bits
-
# in the collection have the same access value.
-
#
-
# If you are worried about hitting the case where some bits have different values then
-
# use access!, but this will be a bit less efficient
-
2
def access(value = nil)
-
70
if value.nil?
-
70
first.access
-
else # set access
-
each { |b| b.set_access(value) }
-
self
-
end
-
end
-
-
# Like access but will raise an error if not all bits in the collection have the same
-
# access value
-
2
def access!
-
val = access
-
if any? { |b| b.access != val }
-
fail 'Not all bits the collection have the same access value!'
-
end
-
val
-
end
-
-
# Returns the description of the given bit(s) if any, if none then an empty array
-
# will be returned
-
#
-
# **Note** Adding a description field will override any comment-driven documentation
-
# of a bit collection (ie markdown style comments)
-
2
def description(bitname = nil, options = {})
-
58
bitname, options = nil, bitname if bitname.is_a?(Hash)
-
58
if name == :unknown
-
[]
-
else
-
58
@reg.description(name, options)
-
end
-
end
-
-
2
def full_name(bitname = nil, options = {})
-
9
bitname, options = nil, bitname if bitname.is_a?(Hash)
-
9
unless name == :unknown
-
9
@reg.full_name(name, options)
-
end
-
end
-
-
2
def bit_value_descriptions(_bitname = nil)
-
24
options = _bitname.is_a?(Hash) ? _bitname : {}
-
24
if name == :unknown
-
[]
-
else
-
24
@reg.bit_value_descriptions(name, options)
-
end
-
end
-
-
# Returns a dummy bit collection that is populated with un-writable bits that will
-
# read back as 0. This can be useful for padding out spaces in registers with something that
-
# responds like conventional bits.
-
2
def self.dummy(reg, name = nil, options = {})
-
31
name, options = nil, name if name.is_a?(Hash)
-
options = {
-
31
size: 8,
-
pos: 0
-
}.merge(options)
-
31
collection = new(reg, name)
-
31
pos = options[:pos]
-
31
options[:size].times do
-
86
bit = Bit.new(reg, pos, writable: false, feature: :dummy_feature)
-
86
collection << bit
-
86
pos += 1
-
end
-
31
collection
-
end
-
-
2
def contains_bits?
-
2
true
-
end
-
-
2
def inspect
-
"<#{self.class}:#{object_id}>"
-
end
-
-
# Returns the LSB position of the collection
-
2
def position
-
1081
first.position
-
end
-
-
# Returns the data value held by the collection
-
# ==== Example
-
# reg(:control).write(0x55)
-
# reg(:control).data # => 0x55, assuming the reg has the required bits to store that
-
2
def data
-
3814
data = 0
-
3814
shift_out_with_index do |bit, i|
-
10185
return undefined if bit.is_a?(Origen::UndefinedClass)
-
10162
data |= bit.data << i
-
end
-
3791
data
-
end
-
2
alias_method :val, :data
-
2
alias_method :value, :data
-
-
# Returns the inverse of the data value held by the collection
-
2
def data_b
-
# (& operation takes care of Bignum formatting issues)
-
2
~data & ((1 << size) - 1)
-
end
-
-
# Returns the reverse of the data value held by the collection
-
2
def data_reverse
-
2
data = 0
-
2
reverse_shift_out_with_index do |bit, i|
-
64
return undefined if bit.is_a?(Origen::UndefinedClass)
-
64
data |= bit.data << i
-
end
-
2
data
-
end
-
2
alias_method :reverse_data, :data_reverse
-
-
# Supports reg.bit[0] and bitcollection.bit[0]
-
2
def bit
-
68
self
-
end
-
-
# Returns true if the collection contains all bits in the register
-
2
def whole_reg?
-
size == parent.size
-
end
-
-
# Set the data value of the collection within the patgen, but not on silicon - i.e. calling
-
# write will not trigger a pattern write event.
-
2
def write(value, options = {})
-
# If an array is written it means a data value and an overlay have been supplied
-
# in one go...
-
353
if value.is_a?(Array) && !value.is_a?(BitCollection)
-
overlay(value[1])
-
value = value[0]
-
end
-
353
value = value.data if value.respond_to?('data')
-
-
353
with_lsb0 do
-
353
size.times do |i|
-
6974
self[i].write(value[i], options)
-
end
-
end
-
353
self
-
end
-
2
alias_method :data=, :write
-
2
alias_method :value=, :write
-
2
alias_method :val=, :write
-
-
# Sets the unknown attribute on all contained bits
-
2
def unknown=(val)
-
5
each { |bit| bit.unknown = val }
-
end
-
-
# Will tag all bits for read and if a data value is supplied it
-
# will update the expected data for when the read is performed.
-
2
def read(value = nil, options = {}) # :nodoc:
-
# First properly assign the args if value is absent...
-
50
if value.is_a?(Hash)
-
options = value
-
value = nil
-
end
-
50
if value
-
30
value = Reg.clean_value(value)
-
30
write(value, force: true)
-
end
-
50
if options[:mask]
-
42
shift_out_with_index { |bit, i| bit.read if options[:mask][i] == 1 }
-
42
shift_out_with_index { |bit, i| bit.clear_read_flag if options[:mask][i] == 0 }
-
else
-
48
each(&:read)
-
end
-
50
self
-
end
-
2
alias_method :assert, :read
-
-
# Returns a value representing the bit collection / register where a bit value of
-
# 1 means the bit is enabled for the given operation.
-
2
def enable_mask(operation)
-
2
str = ''
-
2
shift_out_left do |bit|
-
32
if operation == :store && bit.is_to_be_stored? ||
-
operation == :read && bit.is_to_be_read? ||
-
operation == :overlay && bit.has_overlay?
-
4
str += '1'
-
else
-
28
str += '0'
-
end
-
end
-
2
str.to_i(2)
-
end
-
-
# Attaches the supplied overlay string to all bits
-
# ==== Example
-
# reg(:data).overlay("data_val")
-
2
def overlay(value)
-
88
each { |bit| bit.overlay(value) }
-
9
self
-
end
-
-
# Resets all bits, this clears all flags and assigns the data value
-
# back to the reset state
-
2
def reset
-
68
each(&:reset)
-
68
self
-
end
-
-
# Shifts out a stream of bit objects corresponding to the size of the BitCollection. i.e. calling
-
# this on a 16-bit register this will pass back 16 bit objects.
-
# If there are holes in the given register then a dummy bit object will be returned that
-
# is not writable and which will always read as 0.
-
# ==== Example
-
# reg(:data).shift_out_left do |bit|
-
# bist_shift(bit)
-
# end
-
2
def shift_out_left
-
# This is functionally equivalent to reverse_shift_out
-
1669
reverse_each { |bit| yield bit }
-
end
-
-
# Same as Reg#shift_out_left but includes the index counter
-
2
def shift_out_left_with_index
-
# This is functionally equivalent to reverse_shift_out_with_index
-
83
reverse_each.with_index { |bit, i| yield bit, i }
-
end
-
-
# Same as Reg#shift_out_left but starts from the LSB
-
2
def shift_out_right
-
# This is functionally equivalent to shift_out, actually sends LSB first
-
77
each { |bit| yield bit }
-
end
-
-
# Same as Reg#shift_out_right but includes the index counter
-
2
def shift_out_right_with_index
-
# This is functionally equivalent to shift_out_with_index
-
83
each_with_index { |bit, i| yield bit, i }
-
end
-
-
# Yields each bit in the register, LSB first.
-
2
def shift_out(&block)
-
30
each(&block)
-
end
-
-
# Yields each bit in the register and its index, LSB first.
-
2
def shift_out_with_index(&block)
-
3958
each_with_index(&block)
-
end
-
-
# Yields each bit in the register, MSB first.
-
2
def reverse_shift_out(&block)
-
5
reverse_each(&block)
-
end
-
-
# Yields each bit in the register and its index, MSB first.
-
2
def reverse_shift_out_with_index(&block)
-
3
reverse_each.with_index(&block)
-
end
-
-
# Returns true if any bits have the read flag set - see Bit#is_to_be_read?
-
# for more details.
-
2
def is_to_be_read?
-
2627
any?(&:is_to_be_read?)
-
end
-
-
# Returns true if any bits have the store flag set - see Bit#is_to_be_stored?
-
# for more details.
-
2
def is_to_be_stored?
-
2581
any?(&:is_to_be_stored?)
-
end
-
-
# Returns true if any bits have the update_required flag set - see Bit#update_required?
-
# for more details.
-
2
def update_required?
-
5
any?(&:update_required?)
-
end
-
-
# Calls the clear_flags method on all bits, see Bit#clear_flags for more details
-
2
def clear_flags
-
77
each(&:clear_flags)
-
77
self
-
end
-
-
# Returns the value you would need to write to the register to put the given
-
# value in these bits
-
2
def setting(value)
-
9
result = 0
-
9
shift_out_with_index do |bit, i|
-
39
result |= bit.setting(value[i])
-
end
-
9
result
-
end
-
-
# Returns true if any bits within are tagged for overlay, supply a specific name
-
# to require a specific overlay only
-
# ==== Example
-
# myreg.overlay("data")
-
# myreg.has_overlay? # => true
-
# myreg.has_overlay?("address") # => false
-
# myreg.has_overlay?("data") # => true
-
2
def has_overlay?(name = nil)
-
5412
any? { |bit| bit.has_overlay?(name) }
-
end
-
-
# Cycles through all bits and returns the last overlay value found, it is assumed therefore
-
# that all bits have the same overlay value when calling this method
-
# ==== Example
-
# myreg.overlay("data")
-
#
-
# myreg.overlay_str # => "data"
-
2
def overlay_str
-
3
result = ''
-
3
each do |bit|
-
48
result = bit.overlay_str if bit.has_overlay?
-
end
-
3
result.to_s
-
end
-
-
# Write the bit value on silicon.
-
# This method will update the data value of the bits and then call $top.write_register
-
# passing the owning register as the first argument.
-
# This method is expected to handle writing the current state of the register to silicon.
-
2
def write!(value = nil, options = {})
-
16
value, options = nil, value if value.is_a?(Hash)
-
16
write(value, options) if value
-
16
if block_given?
-
2
yield size == @reg.size ? @reg : self
-
end
-
16
@reg.request(:write_register, options)
-
16
self
-
end
-
-
# Similar to write! this method will perform the standard read method and then make
-
# a call to $top.read_register(self) with the expectation that this method will
-
# implement a read event in the pattern.
-
# ==== Example
-
# reg(:data).read! # Read register :data, expecting whatever value it currently holds
-
# reg(:data).read!(0x5555) # Read register :data, expecting 0x5555
-
2
def read!(value = nil, options = {})
-
29
value, options = nil, value if value.is_a?(Hash)
-
29
read(value, options) unless block_given?
-
29
if block_given?
-
1
yield size == @reg.size ? @reg : self
-
end
-
29
@reg.request(:read_register, options)
-
29
self
-
end
-
2
alias_method :assert!, :read!
-
-
# Normally whenever a register is processed by the $top.read_register method
-
# it will call Reg#clear_flags to acknowledge that the read has been performed,
-
# which clears the read and store flags for the given bits. Normally however you
-
# want overlays to stick around such that whenever a given bit is written/read its
-
# data is always picked from an overlay.<br>
-
# Call this passing in false for a given register to cause the overlay data to also
-
# be cleared by Reg#clear_flags.
-
# ==== Example
-
# reg(:data).overlay("data_val")
-
# reg(:data).has_overlay? # => true
-
# reg(:data).clear_flags
-
# reg(:data).has_overlay? # => true
-
# reg(:data).sticky_overlay(false)
-
# reg(:data).clear_flags
-
# reg(:data).has_overlay? # => false
-
2
def sticky_overlay(set = true)
-
each { |bit| bit.sticky_overlay = set }
-
self
-
end
-
2
alias_method :sticky_overlays, :sticky_overlay
-
-
# Similar to sticky_overlay this method affects how the store flags are treated by
-
# Reg#clear_flags.<br>
-
# The default is that store flags will get cleared by Reg#clear_flags, passing true
-
# into this method will override this and prevent them from clearing.
-
# ==== Example
-
# reg(:data).sticky_store(true)
-
# reg(:data).store
-
# reg(:data).clear_flags # Does not clear the request to store
-
2
def sticky_store(set = true)
-
each { |bit| bit.sticky_store = set }
-
self
-
end
-
-
# Marks all bits to be stored
-
2
def store(options = {})
-
4
each(&:store)
-
4
self
-
end
-
-
# Marks all bits to be stored and then calls read!
-
2
def store!(options = {})
-
store(options)
-
read!(options)
-
self
-
end
-
-
# Sets the store flag on all bits that already have the overlay flag set
-
# and then calls $top.read_register passing self as the first argument
-
2
def store_overlay_bits!(options = {})
-
store_overlay_bits(options)
-
@reg.request(:read_register, options) # Bypass the normal read method since we don't want to
-
# tag the other bits for read
-
self
-
end
-
-
# Sets the store flag on all bits that already have the overlay flag set
-
2
def store_overlay_bits(options = {})
-
options = { exclude: [], # Pass in an array of any overlays that are to be excluded from store
-
}.merge(options)
-
each do |bit|
-
bit.store if bit.has_overlay? && !options[:exclude].include?(bit.overlay_str)
-
end
-
self
-
end
-
-
# Will yield all unique overlay strings attached to the bits within the collection.
-
# It will also return the number of bits for the overlay (the length) and the current
-
# data value held in those bits.
-
# ==== Example
-
# reg(:control).unique_overlays do |str, length, data|
-
# do_something(str, length, data)
-
# end
-
2
def unique_overlays
-
current_overlay = false
-
length = 0
-
data = 0
-
shift_out_right do |bit|
-
# Init the current overlay when the first one is encountered
-
current_overlay = bit.overlay_str if bit.has_overlay? && !current_overlay
-
-
if bit.has_overlay?
-
if bit.overlay_str != current_overlay
-
yield current_overlay, length, data if current_overlay
-
length = 0
-
data = 0
-
end
-
-
data = data | (bit.data << length)
-
length += 1
-
else
-
yield current_overlay, length, data if current_overlay
-
length = 0
-
data = 0
-
current_overlay = false
-
end
-
end
-
yield current_overlay, length, data if current_overlay
-
end
-
-
# Append a value, for example a block identifier, to all overlays
-
# ==== Example
-
# reg(:data).overlay("data_val")
-
# reg(:data).append_overlays("_0")
-
# reg(:data).overlay_str # => "data_val_0"
-
2
def append_overlays(value)
-
each do |bit|
-
bit.overlay(bit.overlay_str + value) if bit.has_overlay?
-
end
-
self
-
end
-
-
# Delete the contained bits from the parent Register
-
2
def delete
-
2
@reg.delete_bits(self)
-
2
self
-
end
-
-
2
def add_name(name) # :nodoc:
-
4592
if @name == :unknown
-
677
@name = name
-
3915
elsif ![name].flatten.include?(name)
-
@name = [@name, name].flatten
-
end
-
4592
self
-
end
-
-
2
def owner
-
first.owner
-
end
-
-
# All other methods send to bit 0
-
2
def method_missing(method, *args, &block) # :nodoc:
-
58
if first.respond_to?(method)
-
58
if size > 1
-
11
if [:meta, :meta_data, :metadata].include?(method.to_sym) ||
-
first.meta_data_method?(method)
-
11
first.send(method, *args, &block)
-
else
-
fail "Error, calling #{method} on a multi-bit collection is not implemented!"
-
end
-
else
-
47
first.send(method, *args, &block)
-
end
-
else
-
fail "BitCollection does not have a method named #{method}!"
-
end
-
end
-
-
# Recognize that BitCollection responds to some Bit methods via method_missing
-
2
def respond_to?(*args) # :nodoc:
-
3062
sym = args.first
-
3062
first.respond_to?(sym) || super(sym)
-
end
-
-
# Returns true if the values of all bits in the collection are known. The value will be
-
# unknown in cases where the reset value is undefined or determined by a memory location
-
# and where the register has not been written or read to a specific value yet.
-
2
def has_known_value?
-
71
all?(&:has_known_value?)
-
end
-
-
# Returns the reset value of the collection, note that this does not reset the register and the
-
# current data is maintained.
-
#
-
# ==== Example
-
# reg(:control).write(0x55)
-
# reg(:control).data # => 0x55
-
# reg(:control).reset_data # => 0x11, assuming the reg was declared with a reset value of 0x11
-
# reg(:control).data # => 0x55
-
2
def reset_data(value = nil)
-
# This method was originally setup to set the reset value by passing an argument
-
105
if value
-
shift_out_with_index { |bit, i| bit.reset_val = value[i] }
-
self
-
else
-
105
data = 0
-
105
shift_out_with_index do |bit, i|
-
527
return bit.reset_data if bit.reset_data.is_a?(Symbol)
-
515
data |= bit.reset_data << i
-
end
-
93
data
-
end
-
end
-
2
alias_method :reset_val, :reset_data
-
2
alias_method :reset_value, :reset_data
-
2
alias_method :reset_data=, :reset_data
-
2
alias_method :reset_val=, :reset_data
-
2
alias_method :reset_value=, :reset_data
-
-
# Modify writable for bits in collection
-
2
def writable(value)
-
shift_out_with_index { |bit, i| bit.writable = (value[i] == 0b1); bit.set_access_from_rw }
-
self
-
end
-
-
# Modify readable for bits in collection
-
2
def readable(value)
-
shift_out_with_index { |bit, i| bit.readable = (value[i] == 0b1); bit.set_access_from_rw }
-
self
-
end
-
-
2
def feature
-
4
feature = []
-
4
feature << fetch(0).feature
-
116
each { |bit| feature << bit.feature if bit.has_feature_constraint? }
-
4
feature = feature.flatten.uniq unless feature.empty?
-
4
feature.delete(nil) if feature.include?(nil)
-
4
if !feature.empty?
-
1
if feature.size == 1
-
1
return feature[0]
-
else
-
return feature.uniq
-
end
-
else
-
3
if Origen.config.strict_errors
-
fail 'No feature found'
-
end
-
return nil
-
end
-
end
-
2
alias_method :features, :feature
-
-
# Return true if there is any feature associated with these bits
-
2
def has_feature_constraint?(name = nil)
-
8
if !name
-
4
any?(&:has_feature_constraint?)
-
else
-
38
any? { |bit| bit.enabled_by_feature?(name) }
-
end
-
end
-
2
alias_method :enabled_by_feature?, :has_feature_constraint?
-
-
2
def enabled?
-
530
all?(&:enabled?)
-
end
-
-
# Returns true if any bits in the collection are writable
-
2
def is_writable?
-
69
any?(&:writable?)
-
end
-
2
alias_method :writable?, :is_writable?
-
-
# Returns true if any bits in the collection are readable
-
2
def is_readable?
-
69
any?(&:readable?)
-
end
-
2
alias_method :readable?, :is_readable?
-
-
# Modify clr_only for bits in collection
-
2
def clr_only(value)
-
shift_out_with_index { |bit, i| bit.clr_only = (value[i] == 0b1) }
-
self
-
end
-
-
# Modify set_only for bits in collection
-
2
def set_only(value)
-
shift_out_with_index { |bit, i| bit.set_only = (value[i] == 0b1) }
-
self
-
end
-
-
# Return nvm_dep value held by collection
-
2
def nvm_dep
-
nvm_dep = 0
-
shift_out_with_index { |bit, i| nvm_dep |= bit.nvm_dep << i }
-
nvm_dep
-
end
-
-
# Clear any w1c set bits back to 0
-
2
def clear_w1c
-
each(&:clear_w1c)
-
self
-
end
-
-
# Clear any start set bits back to 0
-
2
def clear_start
-
each(&:clear_start)
-
self
-
end
-
-
# Provides a string summary of the bit collection / register state that would be
-
# applied to given operation (write or read).
-
# This is mainly intended to be useful when generating pattern comments describing
-
# an upcoming register transaction.
-
#
-
# This highlights not only bit values bit the status of any flags or overlays that
-
# are currently set.
-
#
-
# The data is presented in hex nibble format with individual nibbles are expanded to
-
# binary format whenever all 4 bits do not have the same status - e.g. if only one
-
# of the four is marked for read.
-
#
-
# The following symbols are used to represent bit state:
-
#
-
# X - Bit is don't care (not marked for read)
-
# V - Bit has been tagged with an overlay
-
# S - Bit is marked for store
-
#
-
# @example
-
#
-
# myreg.status_str(:write) # => "0000"
-
# myreg.status_str(:read) # => "XXXX"
-
# myreg[7..4].read(5)
-
# myreg.status_str(:read) # => "XX5X"
-
# myreg[14].read(0)
-
# myreg.status_str(:read) # => "(x0xx)X5X"
-
2
def status_str(operation, options = {})
-
options = {
-
13
mark_overlays: true
-
}.merge(options)
-
13
str = ''
-
13
if operation == :read
-
10
shift_out_left do |bit|
-
145
if bit.is_to_be_stored?
-
18
str += STORE_CHAR
-
127
elsif bit.is_to_be_read?
-
66
if bit.has_overlay? && options[:mark_overlays]
-
15
str += OVERLAY_CHAR
-
else
-
51
if bit.has_known_value?
-
47
str += bit.data.to_s
-
else
-
4
str += UNKNOWN_CHAR
-
end
-
end
-
else
-
61
str += DONT_CARE_CHAR
-
end
-
end
-
3
elsif operation == :write
-
3
shift_out_left do |bit|
-
43
if bit.has_overlay? && options[:mark_overlays]
-
5
str += OVERLAY_CHAR
-
else
-
38
if bit.has_known_value?
-
38
str += bit.data.to_s
-
else
-
str += UNKNOWN_CHAR
-
end
-
end
-
end
-
else
-
fail "Unknown operation (#{operation}), must be :read or :write"
-
end
-
13
make_hex_like(str, (size / 4.0).ceil)
-
end
-
-
# Shifts the data in the collection left by one place. The data held
-
# by the rightmost bit will be set to the given value (0 by default).
-
#
-
# @example
-
# myreg.data # => 0b1111
-
# myreg.shift_left
-
# myreg.data # => 0b1110
-
# myreg.shift_left
-
# myreg.data # => 0b1100
-
# myreg.shift_left(1)
-
# myreg.data # => 0b1001
-
# myreg.shift_left(1)
-
# myreg.data # => 0b0011
-
2
def shift_left(data = 0)
-
4
prev_bit = nil
-
4
reverse_shift_out do |bit|
-
16
prev_bit.write(bit.data) if prev_bit
-
16
prev_bit = bit
-
end
-
4
prev_bit.write(data)
-
4
self
-
end
-
-
# Shifts the data in the collection right by one place. The data held
-
# by the leftmost bit will be set to the given value (0 by default).
-
#
-
# @example
-
# myreg.data # => 0b1111
-
# myreg.shift_right
-
# myreg.data # => 0b0111
-
# myreg.shift_right
-
# myreg.data # => 0b0011
-
# myreg.shift_right(1)
-
# myreg.data # => 0b1001
-
# myreg.shift_right(1)
-
# myreg.data # => 0b1100
-
2
def shift_right(data = 0)
-
29
prev_bit = nil
-
29
shift_out do |bit|
-
116
prev_bit.write(bit.data) if prev_bit
-
116
prev_bit = bit
-
end
-
29
prev_bit.write(data)
-
29
self
-
end
-
-
2
private
-
-
# Converts a binary-like representation of a data value into a hex-like version.
-
# e.g. input => 010S0011SSSS0110 (where S, X or V represent store, don't care or overlay)
-
# output => [010s]3S6 (i.e. nibbles that are not all of the same type are expanded)
-
2
def make_hex_like(regval, size_in_nibbles)
-
13
outstr = ''
-
13
regex = '^(.?.?.?.)'
-
48
(size_in_nibbles - 1).times { regex += '(....)' }
-
13
regex += '$'
-
13
Regexp.new(regex) =~ regval
-
-
13
nibbles = []
-
13
size_in_nibbles.times do |n| # now grouped by nibble
-
48
nibbles << Regexp.last_match[n + 1]
-
end
-
-
13
nibbles.each_with_index do |nibble, i|
-
# If contains any special chars...
-
48
if nibble =~ /[#{UNKNOWN_CHAR}#{DONT_CARE_CHAR}#{STORE_CHAR}#{OVERLAY_CHAR}]/
-
# If all the same...
-
28
if nibble[0] == nibble[1] && nibble[1] == nibble[2] && nibble[2] == nibble[3]
-
21
outstr += nibble[0, 1] # .to_s
-
# Otherwise present this nibble in 'binary' format
-
else
-
7
outstr += "[#{nibble.downcase}]"
-
end
-
# Otherwise if all 1s and 0s...
-
else
-
20
outstr += '%1X' % nibble.to_i(2)
-
end
-
end
-
13
outstr
-
end
-
-
# Cleans up indexed references to pins, e.g. makes these equal:
-
#
-
# bits(:data)[0,1,2,3]
-
# bits(:data)[3,2,1,0]
-
# bits(:data)[0..3]
-
# bits(:data)[3..0]
-
2
def expand_and_order(*indexes)
-
8338
ixs = []
-
8338
indexes.flatten.each do |index|
-
8338
if index.is_a?(Range)
-
20
if index.first > index.last
-
12
ixs << (index.last..index.first).to_a
-
else
-
8
ixs << index.to_a
-
end
-
else
-
8318
ixs << index
-
end
-
end
-
8338
ixs.flatten!
-
# ixs.sort!
-
# convert msb0 numbering (if provided) to lsb0 numbering to get the correct bits
-
8338
if @with_bit_order == :msb0
-
39
ixs.each_index { |i| ixs[i] = size - ixs[i] - 1 }
-
end
-
8338
ixs.sort
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Registers
-
# A container can be used to easily interface register operations to an IPS-style
-
# interface where the container will take care of data alignment and byte enable
-
# calculations.
-
# A container looks and behaves like a register and drivers should be able to
-
# accept a container in place of a regular register.
-
#
-
# Here are some examples:
-
#
-
# include Origen::Registers
-
#
-
# # Name Address Size Bits
-
# add_reg :r0, 4, 8, data => {:bits => 8}
-
# add_reg :r1, 5, 8, data => {:bits => 8}
-
# add_reg :r2, 6, 8, data => {:bits => 8}
-
# add_reg :r3, 7, 8, data => {:bits => 8}
-
#
-
# reg(:r0).write(0xB0)
-
# reg(:r1).write(0xB1)
-
# reg(:r2).write(0xB2)
-
# reg(:r3).write(0xB3)
-
#
-
# big = Container.new
-
# little = Container.new(:endian => :little)
-
#
-
# big.add(reg(:r0)).data # => 0x0000_00B0
-
# little.add(reg(:r0)).data # => 0xB000_0000
-
# big.byte_enable # => 0b0001
-
# little.byte_enable # => 0b1000
-
#
-
# big.empty
-
# big.data # => 0x0000_0000
-
# big.address # => nil
-
# big.add(reg(:r2))
-
# big.address # => 4 (longword aligned)
-
# big.add(reg(:r3)).add(reg(:r1)
-
# big.add.data # => 0xB3B2_B100
-
# big.byte_enable # => 0b1110
-
#
-
# # Treat it like it's a register in drivers:
-
# big.shift_out_left do |bit|
-
# pin(:tdi).drive!(bit.data)
-
# end
-
#
-
# # The address can be overridden
-
# big.empty
-
# big.add(reg(:r2), :address => 10)
-
# big.address # => 8 (longword aligned)
-
#
-
# # Containers can accomodate other containers
-
# big.empty
-
# lower_word = Container.new
-
# lower_word.add(:r0).add(:r1)
-
# big.add(:r3)
-
# lower_word.data # => 0x0000_B1B0
-
# big.data # => 0xB300_0000
-
# big.add(lower_word)
-
# big.data # => 0xB300_B1B0
-
# lower_word.data # => 0x0000_B1B0
-
#
-
# # Contained registers are the same register objects
-
# reg(:r0).write(0x55)
-
# big.data # => 0xB300_B155
-
# lower_word.data # => 0x0000_B155
-
2
class Container
-
# The size of the container in bits
-
2
attr_reader :size
-
# The number of bits represented by an address increment
-
# of the contained registers. For example if the contained registers
-
# have a byte address this will return 8.
-
2
attr_reader :bits_per_address
-
# Returns the currently held registers
-
2
attr_reader :regs
-
2
alias_method :registers, :regs
-
# Set this to a string or an array of strings that represents the name of the object that owns the
-
# container. If present any owned_by? requests made to the container will be
-
# evaluated against this string. If not then the request will be sent to the
-
# first contained register (if present).
-
2
attr_accessor :owned_by
-
-
# @param [Hash] options Options to customize the container
-
# @option options [Integer] :size (32) The size of the container in bits
-
# @option options [Symbol] :endian (:big) The endianness of the container, :big or :little
-
# For example big endian means that 4 a 32-bit container the bytes are arranged
-
# [3,2,1,0] whereas a little endian container would be [0,1,2,3].
-
# @option options [Integer] :bits_per_address (8) The number of bits that will be represented
-
# by an address increment of the given register's addresses
-
2
def initialize(options = {})
-
options = {
-
22
size: 32,
-
endian: :big,
-
bits_per_address: 8
-
}.merge(options)
-
22
@size = options[:size]
-
22
@endian = options[:endian]
-
22
@owned_by = options[:owned_by]
-
22
@bits_per_address = options[:bits_per_address]
-
22
@regs = []
-
22
@addresses = {}
-
end
-
-
2
def contains_bits?
-
1
true
-
end
-
-
# Add the given register to the container, currently there is no
-
# error checking performed to ensure that it doesn't overlap with
-
# any existing contained registers.
-
2
def add(reg, options = {})
-
45
@regs << reg
-
45
addr = options[:address] || options[:addr]
-
45
@addresses[reg] = addr if addr
-
118
@regs.sort_by! { |reg| address_of_reg(reg) }
-
45
self
-
end
-
-
# @api private
-
2
def address_of_reg(reg)
-
781
@addresses[reg] || reg.address
-
end
-
-
# Returns the data held by the contained registers where the data from
-
# each register is shifted into the correct position
-
2
def data
-
17
d = 0
-
17
regs.each do |reg|
-
22
d += (reg.data << bit_shift_for_reg(reg))
-
end
-
17
d
-
end
-
2
alias_method :val, :data
-
2
alias_method :value, :data
-
-
# Data bar, the ones complement of the current data value of the
-
# container
-
2
def data_b
-
4
~data & ((1 << size) - 1)
-
end
-
-
# Remove all registers from the container
-
2
def empty
-
6
@regs = []
-
6
@addresses = {}
-
6
self
-
end
-
-
# Returns the owner of the contained registers (assumed to be the
-
# same for all)
-
2
def owner
-
7
unless @regs.empty?
-
5
@regs.first.owner
-
end
-
end
-
-
# Proxies to the Reg#owned_by? method
-
2
def owned_by?(name)
-
3
if owned_by
-
1
[owned_by].flatten.any? do |al|
-
1
al.to_s =~ /#{name}/i
-
end
-
else
-
2
if @regs.empty?
-
1
false
-
else
-
1
@regs.first.owned_by?(name)
-
end
-
end
-
end
-
-
# Returns the aligned address of the container based on the
-
# address of the currently contained registers
-
2
def address
-
116
unless @regs.empty?
-
112
addr = address_of_reg(@regs.first)
-
112
shift = Math.log(size / bits_per_address, 2)
-
112
(addr >> shift) << shift
-
end
-
end
-
2
alias_method :addr, :address
-
-
# Returns the byte enable required to update the contained registers.
-
2
def byte_enable
-
9
enable = 0
-
9
regs.each do |reg|
-
14
enable_bits = 0.ones_comp(reg.size / bits_per_address)
-
14
enable += (enable_bits << shift_for_reg(reg))
-
end
-
9
enable
-
end
-
-
# @api private
-
2
def local_addr_for_reg(reg)
-
596
address_of_reg(reg) & 0.ones_comp(Math.log(size / bits_per_address, 2))
-
end
-
-
# @api private
-
2
def shift_for_reg(reg)
-
596
if big_endian?
-
422
local_addr_for_reg(reg)
-
else
-
174
(size / bits_per_address) - (local_addr_for_reg(reg) + (reg.size / bits_per_address))
-
end
-
end
-
-
# @api private
-
2
def bit_shift_for_reg(reg)
-
582
shift_for_reg(reg) * bits_per_address
-
end
-
-
2
def big_endian?
-
602
@endian == :big
-
end
-
-
2
def little_endian?
-
2
!big_endian?
-
end
-
-
# Shifts out a stream of bit objects corresponding to the size of the container. i.e. calling
-
# this on a 32-bit container this will pass back 32 bit objects.
-
# If there are holes then a dummy bit object will be returned that
-
# is not writable and which will always read as 0.
-
#
-
# The index is also returned as a second argument. Note that
-
# the position property of the bit is not updated to reflect its position
-
# within the container (it will return its position with its parent
-
# register), therefore the index should be used if the calling code
-
# needs to work out the bit position within the container.
-
2
def shift_out_left
-
3
size.times do |i|
-
96
yield(bit_at_position(size - i - 1), i)
-
end
-
end
-
2
alias_method :shift_out_left_with_index, :shift_out_left
-
-
# Shifts out a stream of bit objects corresponding to the size of the container. i.e. calling
-
# this on a 32-bit container this will pass back 32 bit objects.
-
# If there are holes then a dummy bit object will be returned that
-
# is not writable and which will always read as 0.
-
#
-
# The index is also returned as a second argument. Note that
-
# the position property of the bit is not updated to reflect its position
-
# within the container (it will return its position with its parent
-
# register), therefore the index should be used if the calling code
-
# needs to work out the bit position within the container.
-
2
def shift_out_right
-
3
size.times do |i|
-
96
yield(bit_at_position(i), i)
-
end
-
end
-
2
alias_method :shift_out_right_with_index, :shift_out_right
-
-
# Returns the bit at the given bit position if it exists, otherwise
-
# returns an un-writable bit
-
2
def bit_at_position(i)
-
592
reg = regs.find { |reg| reg_contains_position?(reg, i) }
-
224
if reg
-
192
reg[i - bit_shift_for_reg(reg)]
-
else
-
32
dummy_bit
-
end
-
end
-
-
# @api private
-
2
def reg_contains_position?(reg, position)
-
368
start = bit_shift_for_reg(reg)
-
368
stop = start + reg.size - 1
-
368
position >= start && position <= stop
-
end
-
-
# Returns the bit at the given bit position if it exists, otherwise
-
# returns an un-writable bit
-
2
def [](i)
-
32
bit_at_position(i)
-
end
-
-
# @api private
-
2
def dummy_bit
-
32
@dummy_bit ||= Bit.new(self, 0, writable: false)
-
end
-
-
# Call the clear_flags on all contained registers
-
2
def clear_flags
-
1
@regs.each(&:clear_flags)
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Registers
-
2
class Domain
-
2
attr_accessor :endian
-
2
attr_accessor :name
-
-
2
def initialize(name, options = {})
-
options = {
-
104
endian: :big
-
}.merge(options)
-
104
@name = name
-
104
@endian = options[:endian]
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Registers
-
2
require 'delegate'
-
-
# Thin wrapper around register objects to modify bit number interpretation
-
#
-
# This is provided as a convenience to make user code more readable
-
2
class Msb0Delegator < ::Delegator
-
2
def initialize(reg_object, bits)
-
398
@reg_object = reg_object
-
398
@bits = bits
-
end
-
-
2
def __getobj__
-
1
@reg_object
-
end
-
-
2
def __object__
-
@reg_object
-
end
-
-
2
def __setobj__(obj)
-
1
@reg_object = obj
-
end
-
-
2
def inspect(options = {})
-
4
options[:with_bit_order] = :msb0
-
4
@reg_object.inspect(options)
-
end
-
-
2
def method_missing(method, *args, &block)
-
6
if args.last.is_a?(Hash)
-
args.last[:with_bit_order] = :msb0
-
else
-
6
args << { with_bit_order: :msb0 }
-
end
-
6
@reg_object.method_missing(method, *args, &block)
-
end
-
-
2
def bit(*args)
-
11
@reg_object.bit(args, with_bit_order: :msb0)
-
end
-
2
alias_method :bits, :bit
-
2
alias_method :[], :bit
-
end
-
end
-
end
-
2
require 'json'
-
2
require 'origen/registers/msb0_delegator'
-
2
module Origen
-
2
module Registers
-
# The register class can be used to represent not only h/ware resgisters,
-
# but really any entity which has an address and data component, such as a specific RAM location.<br>
-
# Any registers instantiated through Origen::Registers#add_reg are instances of this class.
-
#
-
# All methods in BitCollection can also be called on a Reg object.
-
2
class Reg
-
2
include Origen::SubBlocks::Path
-
2
include Origen::SubBlocks::Domains
-
-
# These attributes can be defined on a register at definition time and will get applied
-
# to all of its contained bits unless a specific bit has its own definition of the same
-
# attribute
-
REG_LEVEL_ATTRIBUTES = {
-
2
_feature: {},
-
_reset: { aliases: [:res] },
-
_memory: {},
-
_path: { aliases: [:hdl_path] },
-
_abs_path: { aliases: [:absolute_path] },
-
_access: {},
-
_bit_order: {}
-
}
-
-
# Returns the object that own the register.
-
# ==== Example
-
# $soc.reg(:blah).owner # Returns the $soc object
-
2
attr_reader :owner
-
2
alias_method :parent, :owner
-
# The base address of the register, this will be set dynamically
-
# by Origen based on the parent's base address
-
2
attr_accessor :base_address
-
2
attr_writer :address # :nodoc:
-
# Returns an integer representing the number of bits in the register
-
2
attr_reader :size
-
# The register name
-
2
attr_accessor :name
-
# Any feature associated with the register
-
2
attr_accessor :feature
-
-
2
attr_accessor :grows_backwards # :nodoc:
-
2
attr_accessor :lookup # :nodoc:
-
# Returns a full path to the file in which the register was defined
-
2
attr_reader :define_file
-
# Returns any application-specific meta-data attatched to the given register
-
2
attr_accessor :meta
-
2
alias_method :meta_data, :meta
-
2
alias_method :metadata, :meta
-
# If the given register's reset data is backed by memory, the memory address can
-
# be recorded in this attribute
-
2
attr_accessor :memory
-
-
# Normally shouldn't be called directly, instantiate through add_reg
-
# Upon initialization bits are stored as follows:
-
# @bits -
-
# An array of bit objects in position order, @bits[5] corresponds
-
# to the bit as position r
-
# @lookup -
-
# A Hash lookup table for quickly accessing bit objects by name
-
# @lookup = { :bit_or_bus_name => {:pos => 3, :bits => 4} }
-
2
def initialize(owner, address, size, name, options = {}) # :nodoc:
-
398
@owner = owner
-
398
@address = address
-
398
@size = size
-
398
@bits = []
-
398
@lookup = {}
-
398
@name = name
-
398
@init_as_writable = options.delete(:init_as_writable)
-
398
@define_file = options.delete(:define_file)
-
398
@from_placeholder = options.delete(:from_placeholder) || false
-
398
REG_LEVEL_ATTRIBUTES.each do |attribute, _meta|
-
2786
if @from_placeholder
-
1624
instance_variable_set("@#{attribute[1..-1]}", options.delete(attribute))
-
else
-
# If register creation is coming directly from Reg.new, instead of Placeholder,
-
# it may not have attributes with '_' prefix
-
1162
instance_variable_set("@#{attribute[1..-1]}", options.delete(attribute[1..-1].to_sym))
-
end
-
end
-
398
@description_from_api = {}
-
398
description = options.delete(:description)
-
398
if description
-
6
@description_from_api[:_reg] = description.split(/\r?\n/)
-
end
-
398
@meta = default_reg_metadata.merge(options.delete(:meta) || {})
-
-
# Initialize with unwritable bits that read back as zero, can override this
-
# to make all writable by default by setting the :init_writable option to true
-
398
@size.times do |n|
-
9758
@bits << Bit.new(self, n, writable: @init_as_writable, undefined: true)
-
end
-
-
# Internally re-map msb0 register descriptions as lsb0
-
445
options.each_value { |bit_desc| bit_desc[:pos] = @size - bit_desc[:pos] - bit_desc[:bits] } if bit_order == :msb0
-
-
398
add_bits_from_options(options)
-
-
398
@msb0_delegator = Msb0Delegator.new(self, @bits)
-
end
-
-
2
def with_msb0
-
21
@msb0_delegator
-
end
-
-
2
def with_lsb0
-
2
self
-
end
-
-
# Returns the bit order attribute of the register (either :msb0 or :lsb0). If
-
# not explicitly defined on this register it will be inherited from the parent
-
# and will default to :lsb0 at the top-level
-
2
def bit_order
-
472
@bit_order ||= parent.respond_to?(:bit_order) ? parent.bit_order : :lsb0
-
end
-
-
2
def freeze
-
1
bits.each(&:freeze)
-
# Call any methods which cache results to generate the instance variables
-
# before they are frozen
-
1
address
-
1
super
-
end
-
-
2
def bind(bitname, live_parameter)
-
3
unless live_parameter.respond_to?(:is_a_live_parameter?) && live_parameter.is_a_live_parameter?
-
1
fail 'Only live updating parameters should be bound, make sure you have not missed .live in the path to the parameter!'
-
end
-
2
@parameter_bound_bits ||= {}
-
2
@parameter_bound_bits[bitname] = live_parameter
-
end
-
-
2
def has_parameter_bound_bits?
-
26634
@parameter_bound_bits && !@parameter_bound_bits.empty?
-
end
-
-
2
def update_bound_bits
-
14
@updating_bound_bits = true
-
14
@parameter_bound_bits.each do |name, val|
-
26
bits(name).write(val)
-
end
-
14
@updating_bound_bits = false
-
end
-
-
2
def updating_bound_bits?
-
170
@updating_bound_bits
-
end
-
-
2
def inspect(options = {})
-
16
wbo = options[:with_bit_order] || :lsb0
-
16
domsb0 = wbo == :msb0
-
16
dolsb0 = !domsb0
-
16
if wbo != bit_order
-
4
Origen.log.warn "Register displayed with #{wbo} numbering, but defined with #{bit_order} numbering"
-
4
Origen.log.warn 'Access (and display) this register with explicit numbering like this:'
-
4
Origen.log.warn ''
-
4
Origen.log.warn " reg(:#{name}).with_msb0 # bit numbering scheme is msb0"
-
4
Origen.log.warn " reg(:#{name}).with_lsb0 # bit numbering scheme is lsb0 (default)"
-
4
Origen.log.warn " reg(:#{name}) # bit numbering scheme is lsb0 (default)"
-
end
-
-
# This fancy_output option is passed in via option hash
-
# Even better, the output could auto-detect 7-bit vs 8-bit terminal output and adjust the parameter, but that's for another day
-
16
fancy_output = options[:fancy_output].nil? ? true : options[:fancy_output]
-
16
if fancy_output
-
12
horiz_double_line = '═'
-
12
horiz_double_tee_down = '╤'
-
12
horiz_double_tee_up = '╧'
-
12
corner_double_up_left = '╒'
-
12
corner_double_up_right = '╕'
-
12
horiz_single_line = '─'
-
12
horiz_single_tee_down = '┬'
-
12
horiz_single_tee_up = '┴'
-
12
horiz_single_cross = '┼'
-
12
horiz_double_cross = '╪'
-
12
corner_single_down_left = '└'
-
12
corner_single_down_right = '┘'
-
12
vert_single_line = '│'
-
12
vert_single_tee_left = '┤'
-
12
vert_single_tee_right = '├'
-
else
-
4
horiz_double_line = '='
-
4
horiz_double_tee_down = '='
-
4
horiz_double_tee_up = '='
-
4
corner_double_up_left = '.'
-
4
corner_double_up_right = '.'
-
4
horiz_single_line = '-'
-
4
horiz_single_tee_down = '-'
-
4
horiz_single_tee_up = '-'
-
4
horiz_single_cross = '+'
-
4
horiz_double_cross = '='
-
4
corner_single_down_left = '`'
-
4
corner_single_down_right = '\''
-
4
vert_single_line = '|'
-
4
vert_single_tee_left = '<'
-
4
vert_single_tee_right = '>'
-
end
-
16
bit_width = 13
-
16
desc = ["\n0x%X - :#{name}" % address]
-
16
r = size % 8
-
16
if r == 0 # || (size > 8 && domsb0)
-
7
desc << (' ' + corner_double_up_left + ((horiz_double_line * bit_width + horiz_double_tee_down) * 8)).chop + corner_double_up_right
-
else
-
9
desc << (' ' + (' ' * (bit_width + 1) * (8 - r)) + corner_double_up_left + ((horiz_double_line * bit_width + horiz_double_tee_down) * r)).chop + corner_double_up_right
-
end
-
-
# "<#{self.class}: #{self.name}>"
-
16
num_bytes = (size / 8.0).ceil
-
16
num_bytes.times do |byte_index|
-
# Need to add support for little endian regs here?
-
34
byte_number = num_bytes - byte_index
-
34
max_bit = (byte_number * 8) - 1
-
34
min_bit = max_bit - 8 + 1
-
-
# BIT INDEX ROW
-
34
line = ' '
-
34
line_complete = false
-
34
8.times do |i|
-
272
bit_num = (byte_number * 8) - i - 1
-
272
if bit_num > size - 1
-
48
line << ' ' + ''.center(bit_width) unless line_complete
-
else
-
224
if dolsb0
-
168
line << vert_single_line + "#{bit_num}".center(bit_width)
-
else
-
56
line << vert_single_line + "#{size - bit_num - 1}".center(bit_width)
-
end
-
end
-
end
-
34
line += vert_single_line unless line_complete
-
34
desc << line
-
-
# BIT NAME ROW
-
34
line = ' '
-
34
first_done = false
-
34
line_complete = false
-
34
named_bits include_spacers: true do |name, bit, bitcounter|
-
140
if _bit_in_range?(bit, max_bit, min_bit)
-
64
if max_bit > (size - 1) && !first_done
-
9
(max_bit - (size - 1)).times do
-
48
line << ' ' * (bit_width + 1)
-
end
-
end
-
-
64
if bit.size > 1
-
-
36
if name
-
34
if bitcounter.nil?
-
34
bit_name = "#{name}[#{_max_bit_in_range(bit, max_bit, min_bit, options)}:#{_min_bit_in_range(bit, max_bit, min_bit, options)}]"
-
34
bit_span = _num_bits_in_range(bit, max_bit, min_bit)
-
-
else
-
upper = _max_bit_in_range(bit, max_bit, min_bit, options) + bitcounter - bit.size
-
lower = _min_bit_in_range(bit, max_bit, min_bit, options) + bitcounter - bit.size
-
if dolsb0
-
bit_name = "#{name}[#{upper}:#{lower}]"
-
else
-
bit_name = "#{name}[#{upper}:#{lower}]"
-
end
-
bit_span = upper - lower + 1
-
end
-
34
width = (bit_width * bit_span) + bit_span - 1
-
34
if bit_name.length > width
-
line << vert_single_line + "#{bit_name[0..width - 2]}*"
-
else
-
34
line << vert_single_line + bit_name.center(width)
-
end
-
-
else
-
2
bit.shift_out_left do |bit|
-
4
if _index_in_range?(bit.position, max_bit, min_bit)
-
4
line << vert_single_line + ''.center(bit_width)
-
end
-
end
-
end
-
-
else
-
28
if name
-
28
bit_name = "#{name}"
-
28
if bit_name.length > bit_width
-
txt = "#{bit_name[0..bit_width - 2]}*"
-
else
-
28
txt = bit_name
-
end
-
else
-
txt = ''
-
end
-
28
line << vert_single_line + txt.center(bit_width)
-
end
-
end
-
140
first_done = true
-
end
-
34
line += vert_single_line
-
34
desc << line
-
-
# BIT STATE ROW
-
34
line = ' '
-
34
first_done = false
-
34
named_bits include_spacers: true do |name, bit, _bitcounter|
-
140
if _bit_in_range?(bit, max_bit, min_bit)
-
64
if max_bit > (size - 1) && !first_done
-
9
(max_bit - (size - 1)).times do
-
48
line << ' ' * (bit_width + 1)
-
end
-
end
-
-
64
if bit.size > 1
-
36
if name
-
34
if bit.has_known_value?
-
34
value = '0x%X' % bit.val[_max_bit_in_range(bit, max_bit, min_bit).._min_bit_in_range(bit, max_bit, min_bit)]
-
else
-
if bit.reset_val == :undefined
-
value = 'X'
-
else
-
value = 'M'
-
end
-
end
-
34
value += _state_desc(bit)
-
34
bit_span = _num_bits_in_range(bit, max_bit, min_bit)
-
34
width = bit_width * bit_span
-
34
line << vert_single_line + value.center(width + bit_span - 1)
-
else
-
2
bit.shift_out_left do |bit|
-
4
if _index_in_range?(bit.position, max_bit, min_bit)
-
4
line << vert_single_line + ''.center(bit_width)
-
end
-
end
-
end
-
else
-
28
if name
-
28
if bit.has_known_value?
-
28
val = bit.val
-
else
-
if bit.reset_val == :undefined
-
val = 'X'
-
else
-
val = 'M'
-
end
-
end
-
28
value = "#{val}" + _state_desc(bit)
-
28
line << vert_single_line + value.center(bit_width)
-
else
-
line << vert_single_line + ''.center(bit_width)
-
end
-
end
-
end
-
140
first_done = true
-
end
-
34
line += vert_single_line
-
34
desc << line
-
-
34
if size >= 8
-
31
r = size % 8
-
31
if byte_index == 0 && r != 0
-
6
desc << (' ' + corner_double_up_left + ((horiz_double_line * bit_width + horiz_double_tee_down) * (8 - r)).chop + horiz_double_cross + (horiz_single_line * (bit_width + 1) * r)).chop + vert_single_tee_left
-
else
-
25
if byte_index == num_bytes - 1
-
13
desc << (' ' + corner_single_down_left + ((horiz_single_line * bit_width + horiz_single_tee_up) * 8)).chop + corner_single_down_right
-
else
-
12
desc << (' ' + vert_single_tee_right + ((horiz_single_line * bit_width + horiz_single_cross) * 8)).chop + vert_single_tee_left
-
end
-
end
-
else
-
3
desc << (' ' + (' ' * (bit_width + 1) * (8 - size)) + corner_single_down_left + ((horiz_single_line * bit_width + horiz_single_tee_up) * size)).chop + corner_single_down_right
-
end
-
end
-
16
desc.join("\n")
-
end
-
-
# Returns a hash containing all register descriptions that have been parsed so far.
-
#
-
# @api private
-
2
def description_lookup
-
298
@@description_lookup ||= {}
-
end
-
-
# Returns any application specific metadata that has been inherited by the
-
# given register.
-
# This does not account for any overridding that may have been applied to
-
# this register specifically however, use the meta method to get that.
-
2
def default_reg_metadata
-
28826
Origen::Registers.default_reg_metadata.merge(
-
Origen::Registers.reg_metadata[owner.class] || {})
-
end
-
-
2
def bit_value_descriptions(bitname, options = {})
-
options = {
-
24
format: :binary
-
}.merge(options)
-
24
base = case options[:format]
-
when :bin, :binary
-
19
2
-
when :hex, :hexadecimal
-
2
16
-
when :dec, :decimal
-
2
10
-
else
-
1
fail "Unknown integer format: #{options[:format]}"
-
end
-
23
desc = {}
-
23
description(bitname).each do |line|
-
166
if line =~ /^\s*(\d+)\s+\|\s+(.+)/
-
132
desc[Regexp.last_match[1].to_i(base)] = Regexp.last_match[2]
-
end
-
end
-
23
desc
-
end
-
-
# Returns the full name of the register when this has been specified in the register
-
# description like this:
-
#
-
# # ** This is the Register Full Name **
-
# # This register blah blah
-
#
-
# This method will also be called by bit collections to look up the name when
-
# defined in a similar manner in the bit description.
-
#
-
# If no name has been specified this will return nil.
-
2
def full_name(bitname = :_reg, _options = {})
-
23
bitname, options = :_reg, bitname if bitname.is_a?(Hash)
-
23
desc = description(bitname).first
-
# Capture something like this:
-
# ** This is the full name ** - This bit blah blah
-
23
if desc && desc =~ /\s*\*\*\s*([^\*.]*)\s*\*\*/
-
14
Regexp.last_match[1].strip
-
end
-
end
-
-
# Escapes brackets and parenthesis. Helper for description method.
-
2
def escape_special_char(str)
-
9
str.gsub('[', '\[').gsub(']', '\]').gsub('(', '\(').gsub(')', '\)') if str
-
end
-
-
# Returns the description of this register if any, if none then an empty array
-
# will be returned
-
#
-
# **Note** Adding a description field will override any comment-driven documentation
-
# of a register (ie markdown style comments)
-
2
def description(bitname = :_reg, options = {})
-
129
bitname, options = :_reg, bitname if bitname.is_a?(Hash)
-
options = {
-
129
include_name: true,
-
include_bit_values: true
-
}.merge(options)
-
129
if @description_from_api[bitname]
-
23
desc = @description_from_api[bitname]
-
else
-
106
parse_descriptions unless description_lookup[define_file]
-
begin
-
106
desc = description_lookup[define_file][name][bitname] || []
-
rescue
-
desc = []
-
end
-
end
-
129
desc = desc.reject do |line|
-
348
if bitname != :_reg
-
263
unless options[:include_bit_values]
-
12
!!(line =~ /^\s*(\d+)\s+\|\s+(.+)/)
-
end
-
else
-
85
false
-
end
-
end
-
129
if desc.first
-
79
unless options[:include_name]
-
9
desc[0] = desc.first.sub(/\s*\*\*\s*#{escape_special_char(full_name(bitname))}\s*\*\*\s*-?\s*/, '')
-
end
-
end
-
9
desc.shift while desc.first && desc.first.strip.empty?
-
129
desc.pop while desc.last && desc.last.strip.empty?
-
129
desc
-
end
-
2
alias_method :descriptions, :description
-
-
# @api private
-
2
def parse_descriptions
-
5
desc = []
-
5
unless File.exist?(define_file)
-
return desc
-
end
-
5
File.readlines(define_file).each do |line|
-
796
if line =~ /^\s*#(.*)/
-
53
desc << Regexp.last_match[1].strip
-
# https://rubular.com/r/D8lg2P5kK1 https://rubular.com/r/XP4ydPV8Fd
-
743
elsif line =~ /^\s*reg\(?\s*[:"'](\w+)["']?\s*,.*\sdo/ || line =~ /^\s*.*add_reg\(?\s*[:"'](\w+)["']?\s*,.*/
-
16
@current_reg_name = Regexp.last_match[1].to_sym
-
16
description_lookup[define_file] ||= {}
-
16
description_lookup[define_file][@current_reg_name] ||= {}
-
16
description_lookup[define_file][@current_reg_name][:_reg] = desc.dup
-
16
desc = []
-
# https://www.rubular.com/r/7FidbC1JRA
-
727
elsif @current_reg_name && line =~ /^\s*(add_bit|bit|reg\.bit)s?\(?\s*\d+\.?\.?\d*\s*,\s*:(\w+)/
-
38
description_lookup[define_file][@current_reg_name][Regexp.last_match[2].to_sym] = desc.dup
-
38
desc = []
-
else
-
689
desc = []
-
end
-
end
-
end
-
-
2
def contains_bits?
-
24
true
-
end
-
-
# @api private
-
2
def add_bits_from_options(options = {}) # :nodoc:
-
# options is now an array for split bit groups or a hash if single bit/range bits
-
# Now add the requested bits to the register, removing the unwritable bits as required
-
398
options.each do |bit_id, bit_params|
-
647
if bit_params.is_a? Hash
-
634
description = bit_params.delete(:description)
-
634
if description
-
17
@description_from_api[bit_id] = description.split(/\r?\n/)
-
end
-
634
bind(bit_id, bit_params.delete(:bind)) if bit_params[:bind]
-
634
position = bit_params[:pos] || 0
-
634
num_bits = bit_params[:bits] || 1
-
634
if @reset
-
19
if @reset.is_a?(Symbol)
-
8
bit_params[:res] = @reset
-
else
-
11
bit_params[:res] = @reset[(num_bits + position - 1), position]
-
end
-
end
-
634
bit_params[:access] = @access if bit_params[:access].nil?
-
634
bit_params[:res] = bit_params[:data] if bit_params[:data]
-
634
bit_params[:res] = bit_params[:reset] if bit_params[:reset]
-
634
if num_bits == 1
-
240
add_bit(bit_id, position, bit_params) # and add the new one
-
else
-
394
add_bus(bit_id, position, num_bits, bit_params)
-
end
-
13
elsif bit_params.is_a? Array
-
-
51
description = bit_params.map { |h| h.delete(:description) }.compact.join("\n")
-
13
unless description.empty?
-
@description_from_api[bit_id] = description.split(/\r?\n/)
-
end
-
13
add_bus_scramble(bit_id, bit_params)
-
end
-
end
-
398
self
-
end
-
-
# This method is called whenever reg.clone is called to make a copy of a given register.
-
# Ruby will correctly copy all instance variables but it will not drill down to copy nested
-
# attributes, like the bits contained in @bits.
-
# This function will therefore correctly clone all bits contained in the register also.
-
2
def initialize_copy(orig) # :nodoc:
-
106
@bits = []
-
106
orig.bits.each do |bit|
-
5054
@bits << bit.clone
-
end
-
106
@lookup = orig.lookup.clone
-
106
self
-
end
-
-
# Returns a dummy register object that can be used on the fly, this can sometimes
-
# be useful to configure an intricate read operation.
-
# ==== Example
-
# # Read bit 5 of RAM address 0xFFFF1280
-
# dummy = Reg.dummy # Create a dummy reg to configure the read operation
-
# dummy.address = 0xFFFF1280 # Set the address
-
# dummy.bit(5).read!(1) # Read bit 5 expecting a 1
-
2
def self.dummy(size = 16)
-
69
Reg.new(self, 0, size, :dummy, init_as_writable: true)
-
end
-
-
# Returns each named bit collection contained in the register,
-
2
def named_bits(options = {})
-
options = {
-
104
include_spacers: false
-
}.merge(options)
-
104
result = []
-
-
# test if @lookup has any values stored as an array
-
# if so it means there is a split group of bits
-
# process that differently to a single bit or continuous range of bits
-
# which are typically stored in a hash
-
-
104
split_bits = false
-
438
@lookup.each { |_k, v| split_bits = true if v.is_a? Array }
-
-
104
if split_bits == false
-
100
current_pos = size
-
-
# Sort by position
-
406
@lookup.sort_by { |_name, details| -details[:pos] }.each do |name, details|
-
306
pos = details[:bits] + details[:pos]
-
306
if options[:include_spacers] && (pos != current_pos)
-
22
collection = BitCollection.dummy(self, nil, size: current_pos - pos, pos: pos)
-
22
unless collection.size == 0
-
22
if block_given?
-
18
yield nil, collection
-
else
-
4
result << [nil, collection]
-
end
-
end
-
end
-
306
collection = BitCollection.new(self, name)
-
306
details[:bits].times do |i|
-
1524
collection << @bits[details[:pos] + i]
-
end
-
306
unless collection.size == 0
-
306
if block_given?
-
243
yield name, collection
-
else
-
63
result << [name, collection]
-
end
-
end
-
306
current_pos = details[:pos]
-
end
-
100
if options[:include_spacers] && (current_pos != 0)
-
6
collection = BitCollection.dummy(self, nil, size: current_pos, pos: 0)
-
6
unless collection.size == 0
-
6
if block_given?
-
2
yield nil, collection
-
else
-
4
result << [nil, collection]
-
end
-
end
-
end
-
4
elsif split_bits == true # if there are split bits, need to convert all register bit values to array elements to allow sorting
-
-
# if the register has bits split up across it, then store the bits in order of decreasing reg position
-
# but first, stuff all the bits in a simple array, as single bits, or ranges of bits
-
-
4
@lookup_splits = []
-
4
@lookup.each do |k, v|
-
28
tempbit = {}
-
28
bitcounter = {}
-
28
if v.is_a? Hash
-
# then this is already a single bit or a continuous range so just stuff it into the array
-
8
tempbit[k] = v
-
8
@lookup_splits << tempbit.clone
-
20
elsif v.is_a? Array
-
# if the bitgroup is split, then decompose into single bits and continuous ranges
-
20
v.each_with_index do |bitdetail, _i|
-
56
if bitcounter.key?(k)
-
36
bitcounter[k] = bitcounter[k] + bitdetail[:bits]
-
else
-
20
bitcounter[k] = bitdetail[:bits]
-
end
-
56
tempbit[k] = bitdetail
-
56
@lookup_splits << tempbit.clone
-
end
-
end
-
28
if v.is_a? Array
-
20
@lookup_splits.each_with_index do |_e, q|
-
172
groupname = @lookup_splits[q].to_a[0][0]
-
172
if groupname == k
-
56
@lookup_splits[q][groupname][:bitgrouppos] = bitcounter[groupname] if groupname == k
-
56
bitcounter[groupname] = bitcounter[groupname] - @lookup_splits[q][groupname][:bits]
-
end
-
end
-
end
-
end
-
# Now sort the array in descending order
-
# Does adding the bitgrouppos need to happen after the sort ?
-
4
@lookup_splits = @lookup_splits.sort do |a, b|
-
172
b.to_a[0][1][:pos] <=> a.to_a[0][1][:pos]
-
end
-
-
4
current_pos = size
-
4
countbits = {} # if countbits.method == nil
-
-
4
@master = {}
-
4
bitgroup = {}
-
4
bitinfo = {}
-
4
info = {}
-
-
4
@lookup_splits.each_with_index do |hash, _i|
-
64
name = hash.to_a[0][0]
-
64
details = hash.to_a[0][1]
-
64
bitcounter = hash.to_a[0][1][:bitgrouppos]
-
64
pos = details[:bits] + details[:pos]
-
64
if options[:include_spacers] && (pos != current_pos)
-
collection = BitCollection.dummy(self, nil, size: current_pos - pos, pos: pos)
-
unless collection.size == 0
-
if block_given?
-
yield nil, collection, bitcounter
-
else
-
result << [nil, collection, bitcounter]
-
end
-
end
-
end
-
64
collection = BitCollection.new(self, name)
-
64
details[:bits].times do |i|
-
64
collection << @bits[details[:pos] + i]
-
end
-
64
unless collection.size == 0
-
64
if block_given?
-
64
yield name, collection, bitcounter
-
else
-
result << [name, collection, bitcounter]
-
end
-
end
-
64
current_pos = details[:pos]
-
end
-
4
if options[:include_spacers] && current_pos != 0
-
collection = BitCollection.dummy(self, nil, size: current_pos, pos: 0)
-
unless collection.size == 0
-
if block_given?
-
yield nil, collection, bitcounter
-
else
-
result << [nil, collection, bitcounter]
-
end
-
end
-
end
-
end
-
104
unless block_given?
-
19
result
-
end
-
end
-
-
# Returns each named bit collection contained in self
-
2
def reverse_named_bits(_options = {})
-
bits = []
-
named_bits { |name, bit| bits << [name, bit] }
-
bits.each do |bit|
-
yield bit[0], bit[1]
-
end
-
end
-
-
# Returns an array of occupied bit positions
-
# ==== Example
-
# reg :fstat, @base + 0x0000, :size => 8 do
-
# bit 7, :ccif
-
# bit 6, :rdcolerr
-
# bit 5, :accerr
-
# bit 4, :pviol
-
# bit 0, :mgstat0
-
# end
-
# regs(:fstat).used_bits
-
# => [0, 4, 5, 6, 7]
-
#
-
# ==== Example
-
# reg :aguahb2, @base + 0x2A, :size => 8 do
-
# bit 5..2, :m0b_hbstrb, :reset => 0x0
-
# bit 1..0, :m0b_htrans, :reset => 0x2
-
# end
-
# regs(:aguahb2).used_bits
-
# => [0, 1, 2, 3, 4, 5]
-
2
def used_bits(_options = {})
-
12
used_bits = []
-
12
named_bits do |_name, bit|
-
28
used_bits << bit.position if bit.size == 1
-
28
if bit.size > 1
-
18
used_bits << ((bit.position)..(bit.position + bit.size - 1)).to_a
-
end
-
end
-
12
used_bits.flatten!
-
12
used_bits.sort!
-
12
used_bits
-
end
-
-
# Returns true if any named_bits exist, false if used_bits is an empty array
-
2
def used_bits?(_options = {})
-
3
used_bits.size > 0
-
end
-
-
# Returns an array of unoccupied bit positions
-
# ==== Example
-
# reg :fstat, @base + 0x0000, :size => 8 do
-
# bit 7, :ccif
-
# bit 6, :rdcolerr
-
# bit 5, :accerr
-
# bit 4, :pviol
-
# bit 0, :mgstat0
-
# end
-
# regs(:fstat).empty_bits
-
# => [1, 2, 3]
-
#
-
# ==== Example
-
# reg :aguahb2, @base + 0x2A, :size => 8 do
-
# bit 5..2, :m0b_hbstrb, :reset => 0x0
-
# bit 1..0, :m0b_htrans, :reset => 0x2
-
# end
-
# regs(:aguahb2).empty_bits
-
# => [6, 7]
-
2
def empty_bits(_options = {})
-
7
array_span = (0..(size - 1)).to_a
-
7
empty_bits = array_span - used_bits
-
7
empty_bits
-
end
-
-
# Returns true if any named_bits exist, false if used_bits is an empty array
-
2
def empty_bits?(_options = {})
-
5
empty_bits.size > 0
-
end
-
-
# Proxies requests from bit collections to the register owner
-
2
def request(operation, options = {}) # :nodoc:
-
45
if operation == :read_register
-
29
object = reader
-
29
(Origen.top_level || owner).read_register_missing!(self) unless object
-
else
-
16
object = writer
-
16
(Origen.top_level || owner).write_register_missing!(self) unless object
-
end
-
45
if tester && tester.respond_to?(operation)
-
25
tester.send(operation, self, options) do
-
25
object.send(operation, self, options)
-
end
-
else
-
20
object.send(operation, self, options)
-
end
-
45
self
-
end
-
-
# Returns the object that will be responsible for writing the given register
-
2
def writer
-
347
@writer ||= lookup_operation_handler(:write_register)
-
end
-
-
# Returns the object that will be responsible for reading the given register
-
2
def reader
-
107
@reader ||= lookup_operation_handler(:read_register)
-
end
-
-
# @api private
-
2
def lookup_operation_handler(operation)
-
# Could have made the controller be the owner when assigned above, but may have run
-
# into problems with the reg meta data stuff
-
163
reg_owner = owner.respond_to?(:controller) && owner.controller ? owner.controller : owner
-
163
if reg_owner.respond_to?(operation)
-
22
reg_owner
-
141
elsif reg_owner.respond_to?(:owner) && reg_owner.owner.respond_to?(operation)
-
25
reg_owner.owner
-
116
elsif Origen.top_level && Origen.top_level.respond_to?(operation)
-
28
Origen.top_level
-
end
-
end
-
-
# Returns the relative address of the given register, equivalent to calling
-
# reg.address(:relative => true)
-
2
def offset
-
13
address(relative: true)
-
end
-
-
# Returns the register address added to its current base_address value (if any).
-
#
-
# @param [Hash] options
-
# @option options [Boolean] :relative (false) Return the address without adding the base address (if present)
-
2
def address(options = {})
-
options = {
-
775
relative: false
-
}.merge(options)
-
775
address = @address
-
775
domain_option = options[:domains] || options[:domain]
-
775
@domain_option ||= domain_option unless frozen?
-
# Blow the cache when the domain option changes
-
775
@base_address_applied = nil unless @domain_option == domain_option
-
775
unless @base_address_applied
-
# Give highest priority to the original API which allowed the object
-
# doing register read/write to define a base_address method
-
93
if (writer && writer.methods.include?(:base_address) && writer.method(:base_address).arity != 0) ||
-
(reader && reader.methods.include?(:base_address) && reader.method(:base_address).arity != 0)
-
# This currently assumes that the base address is always the same
-
# for reading and writing
-
35
if writer && writer.respond_to?(:base_address) && writer.method(:base_address).arity != 0
-
35
self.base_address = writer.base_address(self)
-
elsif reader && reader.respond_to?(:base_address) && reader.method(:base_address).arity != 0
-
self.base_address = reader.base_address(self)
-
end
-
else
-
58
o = owner.is_a?(Container) ? owner.owner : owner
-
58
d = domain_option || domains
-
58
if o && o.reg_base_address(domain: d)
-
58
self.base_address = o.reg_base_address(domain: d)
-
end
-
end
-
93
@base_address_applied = true
-
end
-
775
unless options[:relative]
-
758
address += base_address if base_address
-
end
-
775
if options[:address_type]
-
Origen.deprecate 'Specifying the address_type of a register address will be removed from Origen 3'
-
case options[:address_type]
-
when :byte
-
address = address * 2
-
when :word
-
address = address
-
when :longword
-
address = address / 2
-
else
-
fail 'Unknown address type requested!'
-
end
-
end
-
775
address
-
end
-
2
alias_method :addr, :address
-
-
# Returns true if the register owner matches the given name. A match will be detected
-
# if the class names of the register's owner contains the given name.
-
#
-
# Alternatively if the register owner implements a method called reg_owner_alias
-
# then the value that this returns instead will also be considered when checking if the given
-
# name matches. This method can return an array of names if multiple aliases are required.
-
#
-
# Aliases can be useful for de-coupling the commonly used name, e.g. "NVM" from the actual
-
# class name.
-
#
-
# @example
-
# class C90NVM
-
# include Origen::Registers
-
#
-
# def initialize
-
# add_reg :clkdiv, 0x3, 16, :div => {:bits => 8}
-
# end
-
#
-
# end
-
#
-
# reg = C90NVM.new.reg(:clkdiv)
-
# reg.owned_by?(:ram) # => false
-
# reg.owned_by?(:nvm) # => true
-
# reg.owned_by?(:c90nvm) # => true
-
# reg.owned_by?(:c40nvm) # => false
-
# reg.owned_by?(:flash) # => false
-
#
-
# @example Using an alias
-
# class C90NVM
-
# include Origen::Registers
-
#
-
# def initialize
-
# add_reg :clkdiv, 0x3, 16, :div => {:bits => 8}
-
# end
-
#
-
# def reg_owner_alias
-
# "flash"
-
# end
-
#
-
# end
-
#
-
# reg = C90NVM.new.reg(:clkdiv)
-
# reg.owned_by?(:ram) # => false
-
# reg.owned_by?(:nvm) # => true
-
# reg.owned_by?(:c90nvm) # => true
-
# reg.owned_by?(:c40nvm) # => false
-
# reg.owned_by?(:flash) # => true
-
2
def owned_by?(name)
-
40
!!(owner.class.to_s =~ /#{name}/i) || begin
-
30
if owner.respond_to?(:reg_owner_alias)
-
3
[owner.reg_owner_alias].flatten.any? do |al|
-
5
al.to_s =~ /#{name}/i
-
end
-
else
-
27
false
-
end
-
end
-
end
-
-
# Returns true if the register contains a bit(s) matching the given name
-
# ==== Example
-
# add_reg :control, 0x55, :status => {:pos => 1}
-
#
-
# reg(:control).has_bit?(:result) # => false
-
# reg(:control).has_bit?(:status) # => true
-
2
def has_bit?(name)
-
27682
@lookup.include?(name)
-
end
-
2
alias_method :has_bits?, :has_bit?
-
2
alias_method :has_bit, :has_bit?
-
2
alias_method :has_bits, :has_bit?
-
-
# Add a bit to the register, should only be called internally
-
2
def add_bit(id, position, options = {}) # :nodoc:
-
240
options = { data: @bits[position].data, # If undefined preserve any data/reset value that has
-
res: @bits[position].data, # already been applied at reg level
-
}.merge(options)
-
-
240
@lookup[id] = { pos: position, bits: 1, feature: options[:feature] }
-
240
@bits.delete_at(position) # Remove the initial bit from this position
-
-
240
@bits.insert(position, Bit.new(self, position, options))
-
240
self
-
end
-
-
# Add a bus to the register, should only be called internally
-
2
def add_bus(id, position, size, options = {}) # :nodoc:
-
394
default_data = 0
-
394
size.times do |n|
-
5608
default_data |= @bits[position + n].data << n
-
end
-
394
options = { data: default_data, # If undefined preserve any data/reset value that has
-
res: default_data, # already been applied at reg level
-
}.merge(options)
-
-
394
@lookup[id] = { pos: position, bits: size }
-
394
size.times do |n|
-
5608
bit_options = options.dup
-
5608
bit_options[:data] = options[:data][n]
-
5608
if options[:res].is_a?(Symbol)
-
16
bit_options[:res] = options[:res]
-
else
-
5592
bit_options[:res] = options[:res][n]
-
end
-
5608
@bits.delete_at(position + n)
-
5608
@bits.insert(position + n, Bit.new(self, position + n, bit_options))
-
end
-
394
self
-
end
-
-
2
def add_bus_scramble(id, array_of_hashes = [])
-
13
array_of_hashes.each do |options|
-
38
bind(id, options.delete(:bind)) if options[:bind]
-
38
position = options[:pos] || 0
-
38
num_bits = options[:bits] || 1
-
38
size = options[:bits]
-
38
options[:data] = options[:data] if options[:data]
-
38
options[:res] = options[:reset] if options[:reset]
-
38
default_data = 0
-
38
size.times do |n|
-
39
default_data |= @bits[position + n].data << n
-
end
-
38
options = { data: default_data, # If undefined preserve any data/reset value that has
-
res: default_data, # already been applied at reg level
-
}.merge(options)
-
-
38
@lookup[id] = [] if @lookup[id].nil?
-
38
@lookup[id] = @lookup[id].push(pos: position, bits: size)
-
38
size.times do |n|
-
39
bit_options = options.dup
-
39
bit_options[:data] = options[:data][n]
-
39
bit_options[:res] = options[:res][n]
-
39
@bits.delete_at(position + n)
-
39
@bits.insert(position + n, Bit.new(self, position + n, bit_options))
-
end
-
38
self
-
end
-
end
-
-
# Delete the bits in the collection from the register
-
2
def delete_bit(collection)
-
2
[collection.name].flatten.each do |name|
-
2
@lookup.delete(name)
-
end
-
2
collection.each do |bit|
-
16
@bits.delete_at(bit.position) # Remove the bit
-
16
@bits.insert(bit.position, Bit.new(self, bit.position, writable: @init_as_writable))
-
end
-
2
self
-
end
-
2
alias_method :delete_bits, :delete_bit
-
-
# @api private
-
2
def expand_range(range, wbo = :lsb0)
-
44
if range.first > range.last
-
34
range = Range.new(range.last, range.first)
-
end
-
44
range = range.to_a
-
44
range.reverse! if wbo == :msb0
-
44
range.each do |i|
-
566
yield i
-
end
-
end
-
-
# Returns the bit object(s) responding to the given name, wrapped in a BitCollection.
-
# This method also accepts multiple name possibilities, if neither bit exists in
-
# the register it will raise an error, otherwise it will return the first match.
-
# If no args passed in, it will return a BitCollection containing all bits.
-
# If a number is passed in then the bits from those positions are returned.
-
# ==== Example
-
# add_reg :control, 0x55, :status => {:pos => 1, :bits => 2},
-
# :fail => {:pos => 0}
-
#
-
# reg(:control).bit(:fail) # => Returns a BitCollection containing the fail bit
-
# reg(:control).bits(:status) # => Returns a BifCollection containing the status bits
-
# reg(:control).bit(:bist_fail, :fail) # => Returns a BitCollection containing the fail bit
-
# reg(:control).bit(0) # => Returns a BitCollection containing the fail bit
-
# reg(:control).bit(1) # => Returns a BitCollection containing status bit
-
# reg(:control).bit(1,2) # => Returns a BitCollection containing both status bits
-
2
def bit(*args)
-
# allow msb0 bit numbering if requested
-
16642
wbo = :lsb0
-
16642
if args.last.is_a?(Hash)
-
96
wbo = args.last.delete(:with_bit_order) || :lsb0
-
96
args.pop if args.last.size == 0
-
end
-
-
16642
multi_bit_names = false
-
# return get_bits_with_constraint(nil,:default) if args.size == 0
-
16642
constraint = extract_feature_params(args)
-
16642
if constraint.nil?
-
16629
constraint = :default
-
end
-
-
16642
collection = BitCollection.new(self, :unknown, [], with_bit_order: wbo)
-
-
16642
if args.size == 0
-
156
collection.add_name(name)
-
156
@bits.each do |bit|
-
6050
collection << get_bits_with_constraint(bit.position, constraint)
-
end
-
else
-
16486
args.flatten!
-
16486
args.sort!
-
16486
args.reverse! if wbo == :msb0
-
16486
args.each do |arg_item|
-
16527
if arg_item.is_a?(Integer)
-
15954
b = get_bits_with_constraint(arg_item, constraint, with_bit_order: wbo)
-
15954
collection << b if b
-
573
elsif arg_item.is_a?(Range)
-
44
expand_range(arg_item, wbo) do |bit_number|
-
566
collection << get_bits_with_constraint(bit_number, constraint, with_bit_order: wbo)
-
end
-
else
-
529
multi_bit_names = args.size > 1
-
# Reaches here if bit name is specified
-
529
if @lookup.include?(arg_item)
-
524
split_bits = false
-
3857
@lookup.each { |_k, v| split_bits = true if v.is_a? Array }
-
524
coll = get_lookup_feature_bits(arg_item, constraint, split_bits)
-
524
if coll
-
524
coll.each do |b|
-
4436
collection.add_name(arg_item)
-
4436
collection << b
-
end
-
end
-
end
-
end
-
end
-
end
-
16642
if collection.size == 0
-
# Originally Origen returned nil when asking for a bit via an index which does not
-
# exist, e.g. reg[1000] => nil
-
# The args numeric clause here is to maintain that behavior
-
2
if Origen.config.strict_errors && !args.all? { |arg| arg.is_a?(Numeric) }
-
puts "Register #{@name} does not have a bits(s) named :#{args.join(', :')} or it might not be enabled."
-
puts 'This could also be a typo, these are the valid bit names:'
-
puts @lookup.keys
-
fail 'Missing bits error!'
-
end
-
nil
-
else
-
16641
if multi_bit_names
-
5
collection.sort_by!(&:position)
-
5
wbo == :msb0 ? collection.with_msb0 : collection.with_lsb0
-
end
-
16641
wbo == :msb0 ? collection.with_msb0 : collection.with_lsb0
-
end
-
end
-
2
alias_method :bits, :bit
-
2
alias_method :[], :bit
-
-
2
def get_bits_with_constraint(number, params, options = {})
-
22570
options = { with_bit_order: :lsb0 }.merge(options)
-
# remap to lsb0 number to grab correct bit
-
22570
if options[:with_bit_order] == :msb0
-
244
number = size - number - 1
-
end
-
-
22570
return nil unless @bits[number]
-
22569
if (params == :default || !params) && @bits[number].enabled?
-
22329
@bits[number]
-
240
elsif params == :none && !@bits[number].has_feature_constraint?
-
32
@bits[number]
-
208
elsif params == :all
-
48
@bits[number]
-
160
elsif params.class == Array
-
32
params.each do |param|
-
32
unless @bits[number].enabled_by_feature?(param)
-
32
return nil
-
end
-
@bits[number]
-
end
-
128
elsif @bits[number].enabled_by_feature?(params)
-
16
@bits[number]
-
else
-
112
return Bit.new(self, number, writable: false)
-
end
-
end
-
-
2
def get_lookup_feature_bits(bit_name, params, split_group_reg)
-
##
-
524
if split_group_reg == false # if this register has single bits and continuous ranges
-
-
490
if @lookup.include?(bit_name)
-
490
collection = BitCollection.new(self, bit_name)
-
490
(@lookup[bit_name][:bits]).times do |i|
-
4334
collection << @bits[@lookup[bit_name][:pos] + i]
-
end
-
490
if !params || params == :default
-
487
if collection.enabled?
-
485
return collection
-
end
-
3
elsif params == :none
-
unless collection.has_feature_constraint?
-
return collection
-
end
-
3
elsif params == :all
-
1
return collection
-
2
elsif params.class == Array
-
2
if params.all? { |param| collection.enabled_by_feature?(param) }
-
return collection
-
end
-
else
-
1
if collection.enabled_by_feature?(params)
-
1
return collection
-
end
-
end
-
3
return BitCollection.dummy(self, bit_name, size: collection.size, pos: @lookup[bit_name][:pos])
-
else
-
return []
-
end
-
-
34
elsif split_group_reg == true # if this registers has split bits in its range
-
34
if @lookup.is_a?(Hash) # && @lookup.include?(bit_name)
-
34
collection = false
-
34
@lookup.each do |k, v| # k is the bitname, v is the hash of bit data
-
190
if k == bit_name
-
34
collection ||= BitCollection.new(self, k)
-
34
if v.is_a?(Array)
-
28
v.reverse_each do |pb| # loop each piece of bit group data
-
90
(pb[:bits]).times do |i|
-
96
collection << @bits[pb[:pos] + i]
-
end
-
end
-
else
-
6
(v[:bits]).times do |i|
-
6
collection << @bits[v[:pos] + i]
-
end
-
end
-
end
-
end
-
34
if !params || params == :default
-
34
if collection.enabled?
-
34
return collection
-
end
-
elsif params == :none
-
unless collection.has_feature_constraint?
-
return collection
-
end
-
elsif params == :all
-
return collection
-
elsif params.class == Array
-
if params.all? { |param| collection.enabled_by_feature?(param) }
-
return collection
-
end
-
else
-
if collection.enabled_by_feature?(params)
-
return collection
-
end
-
end
-
if @lookup.is_a?(Hash) && @lookup[bit_name].is_a?(Array)
-
return BitCollection.dummy(self, bit_name, size: collection.size, pos: @lookup[bit_name][0][:pos])
-
else
-
return BitCollection.dummy(self, bit_name, size: collection.size, pos: @lookup[bit_name[:pos]])
-
end
-
else
-
return []
-
end
-
end
-
end
-
-
2
def extract_feature_params(args)
-
33179
index = args.find_index { |arg| arg.class == Hash }
-
16642
if index
-
13
params = args.delete_at(index)
-
else
-
16629
params = nil
-
end
-
-
16642
if params
-
13
return params[:enabled_features] || params[:enabled_feature]
-
else
-
return nil
-
end
-
end
-
-
# All other Reg methods are delegated to BitCollection
-
2
def method_missing(method, *args, &block) # :nodoc:
-
816
wbo = :lsb0
-
816
if args.last.is_a?(Hash)
-
9
wbo = args.last[:with_bit_order] if args.last.key?(:with_bit_order)
-
end
-
-
816
if method.to_sym == :to_ary || method.to_sym == :to_hash
-
nil
-
814
elsif meta_data_method?(method)
-
13
extract_meta_data(method, *args)
-
else
-
801
if BitCollection.instance_methods.include?(method)
-
729
to_bit_collection(with_bit_order: wbo).send(method, *args, &block)
-
72
elsif has_bits?(method)
-
72
bits(method, with_bit_order: wbo)
-
else
-
super
-
end
-
end
-
end
-
-
2
def to_bit_collection(options = {})
-
729
BitCollection.new(self, name, @bits, options)
-
end
-
-
# Recognize that Reg responds to all BitCollection methods methods based on
-
# application-specific meta data properties
-
2
def respond_to?(*args) # :nodoc:
-
27610
sym = args.first.to_sym
-
27610
meta_data_method?(sym) || has_bits?(sym) || super(sym) || BitCollection.instance_methods.include?(sym)
-
end
-
-
# Copy overlays from one reg object to another
-
# ==== Example
-
# reg(:data_copy).has_overlay? # => false
-
# reg(:data).overlay("data_val")
-
# reg(:data_copy).copy_overlays_from(reg(:data))
-
# reg(:data_copy).has_overlay? # => true
-
2
def copy_overlays_from(reg, options = {})
-
size.times do |i|
-
source_bit = reg.bit[i]
-
if source_bit.has_overlay?
-
ov = source_bit.overlay_str
-
# If an id has been supplied make sure any trailing ID in the source is
-
# changed to supplied identifier
-
ov.gsub!(/_\d$/, "_#{options[:update_id]}") if options[:update_id]
-
@bits[i].overlay(ov)
-
end
-
end
-
self
-
end
-
-
# Copies data from one reg object to another
-
# ==== Example
-
# reg(:data_copy).data # => 0
-
# reg(:data).write(0x1234)
-
# reg(:data_copy).copy_data_from(reg(:data))
-
# reg(:data_copy).data # => 0x1234
-
2
def copy_data_from(reg)
-
size.times do |i|
-
@bits[i].write(reg.bit[i].data)
-
end
-
self
-
end
-
-
# Copies data and overlays from one reg object to another, it does not copy
-
# read or store flags
-
2
def copy(reg)
-
3
size.times do |i|
-
48
source_bit = reg.bit[i]
-
48
@bits[i].overlay(source_bit.overlay_str) if source_bit.has_overlay?
-
48
@bits[i].write(source_bit.data)
-
end
-
3
self
-
end
-
-
# Returns the BITWISE AND of reg with another reg or a number, the state of
-
# both registers remains unchanged
-
# ==== Example
-
# reg(:data).write(0x5555)
-
# reg(:data2).write(0xFFFF)
-
# reg(:data) & 0xFF00 # => 0x5500
-
# reg(:data) & reg(:data2) # => 0x5555
-
2
def &(val)
-
data & Reg.clean_value(val)
-
end
-
-
# Returns the BITWISE OR of reg with another reg or a number, the state of
-
# both registers remains unchanged
-
2
def |(val)
-
data | Reg.clean_value(val)
-
end
-
-
# Returns the SUM of reg with another reg or a number, the state of
-
# both registers remains unchanged
-
2
def +(val)
-
data + Reg.clean_value(val)
-
end
-
-
# Returns the SUBTRACTION of reg with another reg or a number, the state of
-
# both registers remains unchanged
-
2
def -(val)
-
data - Reg.clean_value(val)
-
end
-
-
# Returns the DIVISION of reg with another reg or a number, the state of
-
# both registers remains unchanged
-
2
def /(val)
-
data / Reg.clean_value(val)
-
end
-
-
# Returns the PRODUCT of reg with another reg or a number, the state of
-
# both registers remains unchanged
-
2
def *(val)
-
data * Reg.clean_value(val)
-
end
-
-
# Cleans an input value, in some cases it could be a register object, or an explicit value.
-
# This will return an explicit value in either case.
-
2
def self.clean_value(value) # :nodoc:
-
30
value = value.val if value.respond_to?('val') # Pull out the data value if a reg object has been passed in
-
30
value
-
end
-
-
# @api private
-
2
def meta_data_method?(method)
-
28424
attr_name = method.to_s.gsub(/\??=?/, '').to_sym
-
28424
if default_reg_metadata.key?(attr_name)
-
18
if method.to_s =~ /\?/
-
4
[true, false].include?(default_reg_metadata[attr_name])
-
else
-
14
true
-
end
-
else
-
28406
false
-
end
-
end
-
-
2
def extract_meta_data(method, *args)
-
13
method = method.to_s.sub('?', '')
-
13
if method =~ /=/
-
1
instance_variable_set("@#{method.sub('=', '')}", args.first)
-
else
-
12
instance_variable_get("@#{method}") || meta[method.to_sym]
-
end
-
end
-
-
# Returns true if the register is constrained by the given/any feature
-
2
def enabled_by_feature?(name = nil)
-
124
if !name
-
52
!!feature
-
else
-
72
if feature.class == Array
-
20
feature.each do |f|
-
28
if f == name
-
17
return true
-
end
-
end
-
3
return false
-
else
-
52
return feature == name
-
end
-
end
-
end
-
2
alias_method :has_feature_constraint?, :enabled_by_feature?
-
-
# Query the owner heirarchy to see if this register is enabled
-
2
def enabled?
-
5794
if feature
-
137
value = false
-
137
current_owner = self
-
137
if feature.class == Array
-
33
feature.each do |f|
-
33
current_owner = self
-
33
loop do
-
99
if current_owner.respond_to?(:owner)
-
66
current_owner = current_owner.owner
-
66
if current_owner.respond_to?(:has_feature?)
-
33
if current_owner.has_feature?(f)
-
value = true
-
break
-
end
-
end
-
else # if current owner does not have a owner
-
33
value = false
-
33
break
-
end
-
end # loop end
-
33
unless value
-
33
if Origen.top_level && \
-
Origen.top_level.respond_to?(:has_feature?) && \
-
Origen.top_level.has_feature?(f)
-
value = true
-
unless value
-
break
-
end
-
end
-
end
-
33
unless value
-
33
break # break if feature not found and return false
-
end
-
end # iterated through all features in array
-
33
return value
-
else # if feature.class != Array
-
104
loop do
-
174
if current_owner.respond_to?(:owner)
-
139
current_owner = current_owner.owner
-
139
if current_owner.respond_to?(:has_feature?)
-
104
if current_owner.has_feature?(feature)
-
69
value = true
-
69
break
-
end
-
end
-
else # if current owner does not have a owner
-
35
value = false
-
35
break
-
end
-
end # loop end
-
104
unless value
-
35
if Origen.top_level && \
-
Origen.top_level.respond_to?(:has_feature?) && \
-
Origen.top_level.has_feature?(feature)
-
value = true
-
end
-
end
-
104
return value
-
end
-
else
-
5657
return true
-
end
-
end
-
-
# Returns true if any of the bits within this register has feature
-
# associated with it.
-
2
def has_bits_enabled_by_feature?(name = nil)
-
if !name
-
bits.any?(&:has_feature_constraint?)
-
else
-
bits.any? { |bit| bit.enabled_by_feature?(name) }
-
end
-
end
-
-
2
def to_json(*args)
-
3
JSON.pretty_generate({
-
name: name,
-
full_name: full_name,
-
address: address,
-
offset: offset,
-
size: size,
-
path: path,
-
reset_value: reset_value,
-
description: description(include_name: false, include_bit_values: false),
-
bits: named_bits.map do |name, bit|
-
{
-
7
name: name,
-
full_name: bit.full_name,
-
position: bit.position,
-
size: bit.size,
-
reset_value: bit.reset_value,
-
access: bit.access,
-
description: bit.description(include_name: false, include_bit_values: false),
-
bit_values: bit.bit_value_descriptions.map do |val, desc|
-
{
-
2
value: val,
-
description: desc
-
}
-
end
-
}
-
end
-
}, *args)
-
end
-
-
2
private
-
-
2
def _state_desc(bits)
-
62
state = []
-
62
unless bits.readable? && bits.writable?
-
if bits.readable?
-
state << 'RO'
-
else
-
state << 'WO'
-
end
-
end
-
62
state << 'Rd' if bits.is_to_be_read?
-
62
state << 'Str' if bits.is_to_be_stored?
-
62
state << 'Ov' if bits.has_overlay?
-
62
if state.empty?
-
62
''
-
else
-
"(#{state.join('|')})"
-
end
-
end
-
-
2
def _max_bit_in_range(bits, max, _min, options = { with_bit_order: false })
-
68
upper = bits.position + bits.size - 1
-
68
if options[:with_bit_order] == :msb0
-
10
bits.size - ([upper, max].min - bits.position) - 1
-
else
-
58
[upper, max].min - bits.position
-
end
-
end
-
-
2
def _min_bit_in_range(bits, _max, min, options = { with_bit_order: false })
-
68
lower = bits.position
-
68
if options[:with_bit_order] == :msb0
-
10
bits.size - ([lower, min].max - lower) - 1
-
else
-
58
[lower, min].max - bits.position
-
end
-
end
-
-
# Returns true if some portion of the given bits falls
-
# within the given range
-
2
def _bit_in_range?(bits, max, min)
-
280
upper = bits.position + bits.size - 1
-
280
lower = bits.position
-
280
!((lower > max) || (upper < min))
-
end
-
-
# Returns the number of bits from the given bits that
-
# fall within the given range
-
2
def _num_bits_in_range(bits, max, min)
-
68
upper = bits.position + bits.size - 1
-
68
lower = bits.position
-
68
[upper, max].min - [lower, min].max + 1
-
end
-
-
# Returns true if the given number is is the
-
# given range
-
2
def _index_in_range?(i, max, min)
-
8
!((i > max) || (i < min))
-
end
-
-
2
def _bit_rw(bits)
-
if bits.readable? && bits.writable?
-
'RW'
-
elsif bits.readable?
-
'RO'
-
elsif bits.writable?
-
'WO'
-
else
-
'X'
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Registers
-
# This is a regular Ruby hash that is used to store collections of Reg objects, it has additional
-
# methods added to allow interaction with the contained registers.
-
# All Ruby hash methods are also available - https://www.ruby-doc.org/core/classes/Hash.html
-
2
class RegCollection < Hash
-
# Returns the object that owns the registers
-
2
attr_reader :owner
-
-
2
def initialize(owner, _options = {})
-
2724
@owner = owner
-
end
-
-
2
def inspect
-
15
map { |k, _v| k }.inspect
-
end
-
-
# Display all regs visually in a console session
-
2
def show
-
2
puts map { |_k, v| v }.inspect
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module RevisionControl
-
2
require 'origen/revision_control/base'
-
2
autoload :DesignSync, 'origen/revision_control/design_sync'
-
2
autoload :Git, 'origen/revision_control/git'
-
2
autoload :Subversion, 'origen/revision_control/subversion'
-
2
autoload :Perforce, 'origen/revision_control/perforce'
-
-
IGNORE_DIRS = %w(
-
2
.ws .lsf log output web coverage .ref .yardoc .collection .bin
-
.session .bundle .tpc pkg tmp .git
-
)
-
-
IGNORE_FILES = %w(
-
2
target/.default release_note.txt *.swp *.swo *~ .bin
-
list/referenced.list tags .ref .pdm/pi_attributes.txt
-
environment/.default
-
)
-
-
# Creates a new revision controller object based on the supplied :local and :remote
-
# options.
-
#
-
# The revision control system will be worked out from the supplied remote value. This method
-
# should therefore be used whenever the remote is a variable that could refer to many different
-
# systems.
-
#
-
# @example
-
#
-
# # I know that the remote refers to DesignSync
-
# rc = Origen::RevisionControl::DesignSync.new remote: "sync//....", local: "my/path"
-
#
-
# # The remote is a variable and I don't know the type
-
# rc = Origen::RevisionControl.new remote: rc_url, local: "my/path"
-
2
def self.new(options = {})
-
case
-
when options[:remote] =~ /^sync/
-
DesignSync.new(options)
-
when options[:remote] =~ /git/
-
Git.new(options)
-
when options[:remote] =~ /^p4/
-
Perforce.new(options)
-
else
-
fail "Could not work out the revision control system for: #{options[:remote]}"
-
end
-
end
-
end
-
end
-
2
require 'open3'
-
2
module Origen
-
2
module RevisionControl
-
# Base class of all revision control system drivers,
-
# all drivers should support the API methods defined here.
-
#
-
# Each instance of this class represents the concept of mapping a local directory to
-
# a remote repository.
-
#
-
# Origen.app.rc will return an instance of this class for the revision control system used
-
# by the current application, the :local attribute will be automatically set to
-
# Origen.root and the :remote attribute will be set per the revision control attributes
-
# defined in config/application.rb.
-
2
class Base
-
# Returns a pointer to the remote location (a Pathname object)
-
2
attr_reader :remote
-
# Returns a pointer to the local location (a Pathname object)
-
2
attr_reader :local
-
# Method to use by Origen::RemoteManager to handle fetching a remote file
-
2
attr_reader :remotes_method
-
-
# rubocop:disable Lint/UnusedMethodArgument
-
-
# All revision control instances represent a remote server mapping
-
# to a local directory, :remote and :local options are required
-
2
def initialize(options = {})
-
2
unless options[:remote] && options[:local]
-
fail ':remote and :local options must be supplied when instantiating a new RevisionControl object'
-
end
-
2
@remote = Pathname.new(options[:remote])
-
2
@local = Pathname.new(options[:local]).expand_path
-
2
@remotes_method = :checkout
-
2
initialize_local_dir(options)
-
end
-
-
# Build the local workspace for the first time.
-
#
-
# This is roughly equivalent to running the checkout command, but should be used in the case where
-
# the local workspace is being setup for the first time.
-
2
def build(options = {})
-
fail "The #{self.class} driver does not support the build method!"
-
end
-
-
# Checkout the given file or directory, it returns a path to the local file.
-
#
-
# The path argument is optional and when not supplied the entire directory will be checked out.
-
#
-
# @param [String, Pathname] path
-
# The path to the remote item to checkout, this can be a pointer to a file or directory
-
# and it can either be a relative path or absolute path to either the local or remote locations.
-
# Multiple values can be supplied and should be separated by a space.
-
# @param [Hash] options Options to customize the operation
-
# @option options [Boolean] :force (false) Force overwrite of any existing local copy
-
# @option options [String] :version (nil) A specific version to checkout, will get latest if
-
# not supplied
-
# @option options [Boolean] :verbose (true) When true will show the command being executed and
-
# the raw output from the underlying revision control tool. When false will show nothing, but
-
# will still raise an error if the underlying command fails.
-
2
def checkout(path = nil, options = {})
-
fail "The #{self.class} driver does not support the checkout method!"
-
end
-
-
# Checkin the given file or directory, it returns a path to the local file.
-
#
-
# The path argument is optional and when not supplied the entire directory will be checked in.
-
#
-
# @param [String, Pathname] path
-
# The path to the remote item to checkout, this can be a pointer to a file or directory
-
# and it can either be a relative path or absolute path to either the local or remote locations.
-
# Multiple values can be supplied and should be separated by a space.
-
# @param [Hash] options Options to customize the operation
-
# @option options [Boolean] :force (false) Force overwrite of any newer version of the file that may
-
# exist, i.e. force checkin the current version to become the latest.
-
# @option options [Boolean] :unmanaged (false) Include files matching the given path that are not currently
-
# managed by the revision control system.
-
# @option options [Boolean] :comment (nil) Optionally supply a checkin comment.
-
# @option options [Boolean] :verbose (true) When true will show the command being executed and
-
# the raw output from the underlying revision control tool. When false will show nothing, but
-
# will still raise an error if the underlying command fails.
-
2
def checkin(path = nil, options = {})
-
fail "The #{self.class} driver does not support the checkin method!"
-
end
-
-
# Returns a hash containing the list of files that have changes compared to the given tag
-
# or compared to the latest version (on the server).
-
#
-
# {
-
# :added => [], # Paths to files that have been added since the previous tag
-
# :removed => [], # Paths to files that have been removed since the previous tag
-
# :changed => [], # Paths to files that have changed since the previous tag
-
# :present => true/false, # Convenience attribute for the caller to check if there are any changes, when
-
# # true at least one of the other arrays will contain a value
-
# }
-
#
-
# The dir argument is optional and when not supplied the entire directory will be checked for
-
# changes.
-
#
-
# Note that added files only refers to those files which have been checked into revision control
-
# since the compared to version, it does not refer to unmanaged files in the workspace.
-
# Use the unmanaged method to get a list of those.
-
#
-
# Note also that while a file is considered added or removed depends on the chronological
-
# relationship between the current version (the user's workspace) and the reference version.
-
# If the reference version is older than the current version (i.e. an earlier tag), then an added
-
# file means a file that the current version has and the reference (previous) version did not have.
-
#
-
# However if the reference version is newer than the current version (e.g. when comparing to a newer
-
# tag or the latest version on the server), then an added file means a file that the current
-
# version does not have and which has been added in a newer version of the remote directory.
-
#
-
# @param [String, Pathname] dir
-
# The path to a sub-directory to check for changes, it can either be a relative path or an
-
# absolute path to either the local or remote locations.
-
# @param [Hash] options Options to customize the operation
-
# @option options [String] :version (nil) A specific version to compare against, will compare to
-
# latest if not supplied
-
# @option options [Boolean] :verbose (false) When true will show the command being executed and
-
# the raw output from the underlying revision control tool. When false will show nothing. False
-
# is the default as with this command the user is more concerned with seeing the organized
-
# summary that is returned from this method.
-
2
def changes(dir = nil, options = {})
-
fail "The #{self.class} driver does not support the changes method!"
-
end
-
-
# Returns an array containing the files that have un-committed local changes.
-
#
-
# The dir argument is optional and when not supplied the entire directory will be checked for
-
# changes.
-
#
-
# @param [String, Pathname] dir
-
# The path to a sub-directory to check for changes, it can either be a relative path or an
-
# absolute path to either the local or remote locations.
-
# @param [Hash] options Options to customize the operation
-
# @option options [Boolean] :verbose (false) When true will show the command being executed and
-
# the raw output from the underlying revision control tool. When false will show nothing. False
-
# is the default as with this command the user is more concerned with seeing the organized
-
# summary that is returned from this method.
-
2
def local_modifications(dir = nil, options = {})
-
fail "The #{self.class} driver does not support the local_modifications method!"
-
end
-
-
# Returns an array containing the list of files that are present in the given directory but
-
# which are not managed by the revision control system.
-
#
-
# The dir argument is optional and when not supplied the entire directory will be checked for
-
# unmanaged files.
-
#
-
# @param [String, Pathname] dir
-
# The path to a sub-directory to check for unmanaged files, it can either be a relative path or an
-
# absolute path to either the local or remote locations.
-
# @param [Hash] options Options to customize the operation
-
# @option options [Boolean] :verbose (false) When true will show the command being executed and
-
# the raw output from the underlying revision control tool. When false will show nothing. False
-
# is the default as with this command the user is more concerned with seeing the organized
-
# summary that is returned from this method.
-
2
def unmanaged(dir = nil, options = {})
-
fail "The #{self.class} driver does not support the unmanaged method!"
-
end
-
-
# Returns the command the user must run to execute a diff of the current version of the given file
-
# against the given version of it.
-
#
-
# @param [String, Pathname] file
-
# The local path to the file to be compared.
-
# @param [String] version
-
# The version of the file to compare to.
-
2
def diff_cmd(file, version)
-
fail "The #{self.class} driver does not support the diff_cmd method!"
-
end
-
-
# Returns what is considered to be the top-level root directory by the revision control system.
-
#
-
# In the case of an application's revision controller (returned by Origen.app.rc) this method will often
-
# return the same directory as Origen.root. However in some cases an application owner may choose to store
-
# their application in a sub directory of a larger project entity that is revision controlled. In that
-
# case Origen.root will return the sub directory and this method will return the top-level directory
-
# of the wider project.
-
2
def root
-
fail "The #{self.class} driver does not support the root method!"
-
end
-
-
# Returns the name of the current branch in the local workspace
-
2
def current_branch
-
fail "The #{self.class} driver does not support the current_branch method!"
-
end
-
-
# Returns true if the revision controller object uses Design Sync
-
2
def dssc?
-
is_a?(DesignSync)
-
end
-
2
alias_method :design_sync?, :dssc?
-
-
# Returns true if the revision controller object uses Git
-
2
def git?
-
186
is_a?(Git) # :-)
-
end
-
-
# Returns true if the revision controller object uses Perforce
-
2
def p4?
-
is_a?(Perforce)
-
end
-
2
alias_method :perforce?, :p4?
-
-
# Returns true if the revision controller object uses Subversion
-
2
def svn?
-
is_a?(Subversion) # :-)
-
end
-
2
alias_method :subversion?, :svn?
-
-
# rubocop:enable Lint/UnusedMethodArgument
-
-
2
private
-
-
2
def clean_path(path = nil, options = {})
-
2
path, options = nil, path if path.is_a?(Hash)
-
2
if path
-
paths = to_local(path)
-
else
-
2
paths = [local.to_s]
-
end
-
2
[paths, options]
-
end
-
-
# Converts a given path string to files/directories to an array of absolute paths to the
-
# resources within the local directory.
-
# The input can either contain a path to the local directory, or the remote.
-
#
-
# @example
-
# to_local("config/application.rb sync://sync-15088:15088/Projects/common_tester_blocks/origen/lib")
-
# # => ["/home/r49409/origen/config/application.rb", "/home/r49409/origen/lib"]
-
2
def to_local(path)
-
local_abs_paths = []
-
path.to_s.split(/\s+/).each do |p|
-
if p =~ /^#{remote}/
-
p.sub!(/^#{remote}/, '')
-
p.slice!(0) if p =~ /^\//
-
p = "#{local}/#{p}"
-
else
-
if Pathname.new(p).absolute?
-
# No action required
-
else
-
p = "#{local}/#{p}"
-
end
-
end
-
local_abs_paths << p
-
end
-
local_abs_paths
-
end
-
-
# If the supplied tag looks like a semantic version number, then make sure it has the
-
# 'v' prefix
-
2
def prefix_tag(tag)
-
tag = Origen::VersionString.new(tag)
-
if tag.semantic?
-
tag.prefixed
-
else
-
tag
-
end
-
end
-
-
2
def initialize_local_dir(options = {})
-
2
FileUtils.mkdir_p(local.to_s) unless local.exist?
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module RevisionControl
-
2
class Git < Base
-
# Returns the origin for the PWD
-
2
def self.origin
-
2
git('remote --verbose', verbose: false).each do |remote|
-
2
if remote =~ /^origin\s+([^\s]+)/
-
2
return Regexp.last_match(1)
-
end
-
end
-
nil
-
end
-
-
# Returns the Git version number from the current runtime environment (as a string)
-
2
def self.version
-
@version ||= begin
-
version = nil
-
git('--version', verbose: false).each do |line|
-
if line =~ /git version (\d+(\.\d+)+)/
-
version = Regexp.last_match(1)
-
break
-
end
-
end
-
if version
-
version
-
else
-
Origen.log.warning 'Failed to determine the current Git version, proceeding by assuming version 2.0.0'
-
'2.0.0'
-
end
-
end
-
end
-
-
2
def build(options = {})
-
if Dir["#{local}/*"].empty? || options[:force]
-
FileUtils.rm_rf(local.to_s)
-
# Not using the regular 'git' method here since the local dir doesn't exist to CD into
-
system "git clone #{remote} #{local}"
-
else
-
fail "The requested workspace is not empty: #{local}"
-
end
-
end
-
-
2
def checkout(path = nil, options = {})
-
paths, options = clean_path(path, options)
-
# Pulls latest metadata from server, does not change workspace
-
git 'fetch', options
-
-
version = options[:version] || current_branch
-
-
if version == 'HEAD'
-
puts "Sorry, but you are not currently on a branch and I don't know which branch you want to checkout"
-
puts 'Please supply a branch name as the version to checkout the latest version of it, e.g. origen rc co -v develop'
-
exit 1
-
end
-
-
if options[:force]
-
version = "origin/#{version}" if remote_branch?(version)
-
if paths == [local.to_s]
-
git "reset --hard #{version}", options
-
else
-
git 'reset HEAD'
-
git 'pull', options
-
git "checkout #{version} #{paths.join(' ')}", options
-
end
-
else
-
if paths.size > 1 || paths.first != local.to_s
-
fail 'The Git driver does not support partial merge checkout, it has to be the whole workspace'
-
end
-
git 'reset HEAD'
-
res = git 'stash', options
-
stashed = !res.any? { |l| l =~ /^No local changes to save/ }
-
git 'pull', options
-
git "checkout #{version}", options
-
if stashed
-
result = git 'stash pop', { check_errors: false }.merge(options)
-
conflicts = []
-
result.each do |line|
-
if line =~ /CONFLICT.* (.*)$/
-
conflicts << Regexp.last_match(1)
-
end
-
end
-
git 'reset HEAD'
-
unless conflicts.empty?
-
Origen.log.info ''
-
Origen.log.error 'Your local changes could not automatically merged into the following files, open them to fix the conflicts:'
-
conflicts.each do |conflict|
-
Origen.log.error " #{conflict}"
-
end
-
end
-
end
-
end
-
paths
-
end
-
-
2
def checkin(path = nil, options = {})
-
paths, options = clean_path(path, options)
-
# Can't check in unless we have the latest
-
if options[:force] && !options[:initial]
-
# Locally check in the given files
-
checkin(paths.join(' '), no_push: true, verbose: false, comment: options[:comment])
-
local_rev = current_commit(short: false)
-
# Pull latest
-
checkout
-
# Restore the given files to our previous version
-
# Errors are ignored here since this can fail if the given file didn't exist until now,
-
# in that case we already implicitly have the previous version
-
git("checkout #{local_rev} -- #{paths.join(' ')}", check_errors: false)
-
# Then proceed with checking them in as latest
-
else
-
checkout unless options[:initial]
-
end
-
cmd = 'add'
-
if options[:unmanaged]
-
cmd += ' -A'
-
else
-
cmd += ' -u' unless options[:unmanaged]
-
end
-
cmd += " #{paths.join(' ')}"
-
git cmd, options
-
if changes_pending_commit?
-
cmd = 'commit'
-
if options[:comment] && !options[:comment].strip.empty?
-
cmd += " -m \"#{options[:comment].strip}\""
-
else
-
cmd += " -m \"No comment!\""
-
end
-
if options[:author]
-
if options[:author].respond_to?(:name_and_email)
-
author = options[:author].name_and_email
-
else
-
author = "#{options[:author]} <>"
-
end
-
cmd += " --author=\"#{author}\""
-
end
-
if options[:time]
-
cmd += " --date=\"#{options[:time].strftime('%a %b %e %H:%M:%S %Y %z')}\""
-
end
-
git cmd, options
-
end
-
unless options[:no_push]
-
cmd = "push origin #{current_branch}"
-
cmd += ' -u' if options[:initial]
-
git cmd
-
end
-
paths
-
end
-
-
# Returns true if the current user can checkin to the given repo (means has permission
-
# to push in Git terms)
-
2
def can_checkin?
-
# dry run attempting to create a new remote branch named OrigenWritePermissionsTest
-
git('push --dry-run origin origin:refs/heads/OrigenWritePermissionsTest', verbose: false)
-
true
-
rescue
-
false
-
end
-
-
2
def changes(dir = nil, options = {})
-
paths, options = clean_path(dir, options)
-
options = {
-
verbose: false
-
}.merge(options)
-
# Pulls latest metadata from server, does not change workspace
-
git 'fetch', options
-
-
version = options[:version] || 'HEAD'
-
objects = {}
-
-
objects[:added] = git("diff --name-only --diff-filter=A #{version} #{paths.first}", options).map(&:strip)
-
objects[:removed] = git("diff --name-only --diff-filter=D #{version} #{paths.first}", options).map(&:strip)
-
objects[:changed] = git("diff --name-only --diff-filter=M #{version} #{paths.first}", options).map(&:strip)
-
-
objects[:present] = !objects[:added].empty? || !objects[:removed].empty? || !objects[:changed].empty?
-
# Return full paths
-
objects[:added].map! { |i| "#{paths.first}/" + i }
-
objects[:removed].map! { |i| "#{paths.first}/" + i }
-
objects[:changed].map! { |i| "#{paths.first}/" + i }
-
objects
-
end
-
-
2
def local_modifications(dir = nil, options = {})
-
2
paths, options = clean_path(dir, options)
-
options = {
-
2
verbose: false
-
}.merge(options)
-
-
2
cmd = 'diff --name-only'
-
2
dir = " #{paths.first}"
-
2
unstaged = git(cmd + dir, options).map(&:strip)
-
2
staged = git(cmd + ' --cached' + dir, options).map(&:strip)
-
2
unstaged + staged
-
end
-
-
2
def unmanaged(dir = nil, options = {})
-
paths, options = clean_path(dir, options)
-
options = {
-
verbose: false
-
}.merge(options)
-
cmd = "ls-files #{paths.first} --exclude-standard --others"
-
git(cmd, options).map(&:strip)
-
end
-
-
2
def diff_cmd(file, version = nil)
-
if version
-
"git difftool --tool tkdiff -y #{prefix_tag(version)} #{file}"
-
else
-
"git difftool --tool tkdiff -y #{file}"
-
end
-
end
-
-
2
def tag(id, options = {})
-
id = VersionString.new(id)
-
id = id.prefixed if id.semantic?
-
if options[:comment]
-
git "tag -a #{id} -m \"#{options[:comment]}\""
-
else
-
git "tag #{id}"
-
end
-
git "push origin #{id}"
-
end
-
-
2
def root
-
8
Pathname.new(git('rev-parse --show-toplevel', verbose: false).first.strip)
-
end
-
-
2
def current_branch
-
2
git('rev-parse --abbrev-ref HEAD', verbose: false).first
-
end
-
-
2
def current_commit(options = {})
-
options = {
-
2
short: true
-
}.merge(options)
-
2
commit = git('rev-parse HEAD', verbose: false).first
-
2
if options[:short]
-
2
commit[0, 11]
-
else
-
commit
-
end
-
end
-
-
# Returns true if the given tag already exists
-
2
def tag_exists?(tag)
-
git('fetch', verbose: false) unless @all_tags_fetched
-
@all_tags_fetched = true
-
git('tag', verbose: false).include?(tag.to_s)
-
end
-
-
# Returns true if the given string matches a branch name in the remote repo
-
# Origen.app.rc.remote_branch?("master") # => true
-
# Origen.app.rc.remote_branch?("feature/exists") # => true
-
# Origen.app.rc.remote_branch?("feature/does_not_exist") # => false
-
2
def remote_branch?(str)
-
# Github doesn't like the ssh:// for this command, whereas Stash seems
-
# to require it.
-
if github?
-
rem = remote_without_protocol
-
else
-
rem = remote
-
end
-
# check if matches 40 digit hex string followed by branch name
-
git("ls-remote --heads #{remote} #{str}", verbose: false).any? do |line|
-
line =~ /^[0-9a-f]{40}\s+[a-zA-Z]/
-
end
-
end
-
-
2
def initialized?(options = {})
-
2
@hierarchy_searched ||= begin
-
2
path = @local.dup
-
2
until path.root? || File.exist?("#{local}/.git")
-
if File.exist?("#{path}/.git")
-
if options[:allow_local_adjustment]
-
@local = path
-
else
-
fail "Requested local repository #{local} is within existing local repository #{path}"
-
end
-
else
-
path = path.parent
-
end
-
end
-
2
true
-
end
-
2
File.exist?("#{local}/.git") &&
-
2
git('remote -v', verbose: false).any? { |r| r =~ /#{remote_without_protocol_and_user}/ || r =~ /#{remote_without_protocol_and_user.to_s.gsub(':', "\/")}/ } &&
-
6
!git('status', verbose: false).any? { |l| l =~ /^#? ?(Initial commit|No commits yet)$/ }
-
end
-
-
# Delete everything in the given directory, or the whole repo
-
2
def delete_all(dir = nil, options = {})
-
paths, options = clean_path(dir, options)
-
files = git("ls-files #{paths.first}")
-
FileUtils.rm_f files
-
end
-
-
# A class method is provided to fetch the user name since it is useful to have access
-
# to this when outside of an application workspace, e.g. when creating a new app
-
2
def self.user_name
-
2
git('config user.name', verbose: false).first
-
rescue
-
nil
-
end
-
-
# A class method is provided to fetch the user email since it is useful to have access
-
# to this when outside of an application workspace, e.g. when creating a new app
-
2
def self.user_email
-
git('config user.email', verbose: false).first
-
rescue
-
nil
-
end
-
-
2
def user_name
-
self.class.user_name
-
end
-
-
2
def user_email
-
self.class.user_email
-
end
-
-
# Returns true if the remote points to a github url
-
2
def github?
-
!!(remote.to_s =~ /github.com/)
-
end
-
-
2
private
-
-
2
def remote_without_protocol
-
2
Pathname.new(remote.sub(/^.*:\/\//, ''))
-
end
-
-
2
def remote_without_protocol_and_user
-
2
Pathname.new(remote_without_protocol.to_s.sub(/^.*@/, ''))
-
end
-
-
2
def create_gitignore
-
c = Origen::Generator::Compiler.new
-
c.compile "#{Origen.top}/templates/git/gitignore.erb",
-
output_directory: local,
-
quiet: true,
-
check_for_changes: false
-
FileUtils.mv "#{local}/gitignore", "#{local}/.gitignore"
-
end
-
-
2
def changes_pending_commit?
-
!(git('status --verbose', verbose: false).last =~ /^(no changes|nothing to commit|nothing added to commit but untracked files present)/)
-
end
-
-
2
def initialize_local_dir(options = {})
-
2
super
-
2
unless initialized?(options)
-
Origen.log.debug "Initializing Git workspace at #{local}"
-
git 'init'
-
git 'remote remove origin', verbose: false, check_errors: false
-
git "remote add origin #{remote}", check_errors: false
-
end
-
end
-
-
2
def git(command, options = {})
-
20
options[:local] = local
-
20
self.class.git(command, options)
-
end
-
-
# Execute a git operation, the resultant output is returned in an array
-
2
def self.git(command, options = {})
-
options = {
-
24
check_errors: true,
-
verbose: true
-
}.merge(options)
-
24
output = []
-
24
if options[:verbose]
-
Origen.log.info "git #{command}"
-
Origen.log.info ''
-
end
-
24
chdir options[:local] do
-
24
Open3.popen2e("git #{command}") do |_stdin, stdout_err, wait_thr|
-
24
while line = stdout_err.gets
-
30
Origen.log.info line.strip if options[:verbose]
-
30
unless line.strip.empty?
-
28
output << line.strip
-
end
-
end
-
-
24
exit_status = wait_thr.value
-
24
unless exit_status.success?
-
if options[:check_errors]
-
if output.any? { |l| l =~ /Not a git repository/ }
-
fail RevisionControlUninitializedError
-
else
-
fail GitError, "This command failed: 'git #{command}'"
-
end
-
end
-
end
-
end
-
end
-
24
output
-
end
-
-
2
def self.chdir(dir)
-
24
if dir
-
20
Dir.chdir dir do
-
20
yield
-
end
-
else
-
4
yield
-
end
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Specs
-
# Ruby Data Class that contains Creation Information for the IP Block
-
2
class Creation_Info
-
2
attr_accessor :author, :date, :revision, :source, :tool, :tool_version, :ip_version, :ip_block_name
-
-
# Initialize the Creation Info block to store data for latest version of the file.
-
#
-
# ==== Parameters
-
#
-
# * author # Author/Subject Matter Expert for the IP Block
-
# * date # Date that the File was released to Downstream Audiences
-
# ==== Source Information
-
#
-
# * :revision # Revision Information
-
# * :source # Where the Information came from
-
# * :ip_block_name # Block Name for the IP. e.g. DDR for DDRC1, DDRC2; I2C for I2C1, I2C2
-
#
-
# ==== Tool Info
-
#
-
# * :tool # Tool that created the initial XML file
-
# * :version # Version of the Tool that created the XML file
-
#
-
# ==== Example
-
#
-
# Creation_Info.new("author", "07/10/2015", :revision => "5.4", :source => "CSV", :tool => "oRiGeN", :tool_version => "0.0.6")
-
2
def initialize(author, date, ip_version, src_info = {}, tool_info = {})
-
4
@author = author
-
4
@date = date
-
4
@ip_version = ip_version
-
4
@revision = src_info[:revision]
-
4
@source = src_info[:source]
-
4
@ip_block_name = src_info[:ip_block_name]
-
4
@tool = tool_info[:tool]
-
4
@tool_version = tool_info[:version]
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Specs
-
# This class is used to store text information to help with documentation processes
-
2
class Doc_Resource
-
# Mode is part of the 4-D Hash for the Tables. Corresponds to Spec 4-D Hash
-
2
attr_accessor :mode
-
-
# Type is part of the 4-D Hash for the Tables. Corresponds to Spec 4-D Hash
-
# Usual values
-
#
-
# * DC -> Direct Current
-
# * AC -> Alternate Current
-
# * Temp -> Temperature
-
# * Supply -> Supply
-
2
attr_accessor :type
-
-
# SubType is part of the 4-D Hash for the Tables. Corresponds to Spec 4-D Hash
-
2
attr_accessor :sub_type
-
-
# Audience is part of the 4-D Hash for the Tables. Corresponds to Spec 4-D Hash
-
2
attr_accessor :audience
-
-
# Table Title that should appear for the table. If blank, generic Table Title will be used
-
# Hash is created from mode, type, sub_type, and audience.
-
2
attr_accessor :table_title
-
-
# Note References that should be referenced within the table title
-
2
attr_accessor :note_refs
-
-
# Exhibit References that should be referenced within the table title
-
2
attr_accessor :exhibit_refs
-
-
# DITA Formatted Text that appears before the table
-
2
attr_accessor :before_table
-
-
# DITA Formatted Text that appears after the table
-
2
attr_accessor :after_table
-
-
# Documentation Options that will change the appearance of the output.
-
2
attr_accessor :doc_options
-
-
# Initialize the Class
-
2
def initialize(selector = {}, table_title = {}, text = {}, options = {})
-
29
@mode = selector[:mode]
-
29
@type = selector[:type]
-
29
@sub_type = selector[:sub_type]
-
29
@audience = selector[:audience]
-
29
@table_title = table_title[:title]
-
29
@note_refs = table_title[:note_refs]
-
29
@exhibit_refs = table_title[:exhibit_refs]
-
29
@before_table = text[:before]
-
29
@after_table = text[:after]
-
29
@doc_options = options
-
end
-
-
# Converts to an XML file.
-
2
def to_xml
-
tmp = {}
-
tmp['mode'] = @mode unless @mode.nil?
-
tmp['type'] = @type unless @type.nil?
-
tmp['sub_type'] = @sub_type unless @sub_type.nil?
-
tmp['audience'] = @audience unless @audience.nil?
-
doc_resource_ml = Nokogiri::XML::Builder.new do |xml|
-
xml.doc_resource(tmp.each do |t, d|
-
"#{t}=\"#{d}\""
-
end
-
) do
-
unless @table_title.nil? && @note_refs.size == 0 && @exhibit_refs.size == 0
-
unless @note_refs.first.to_s.size == 0
-
unless @exhibit_refs.first.to_s.size == 0
-
xml.title do
-
unless @table_title.nil?
-
xml.Text @table_title.to_s
-
end
-
unless @note_refs.size == 0
-
unless @note_refs.first.to_s.size == 0
-
xml.noteRefs do
-
@note_refs = [@note_refs] unless @note_refs.is_a? Array
-
@note_refs.each do |note_ref|
-
unless note_ref.to_s.size == 0
-
xml.noteRef(href: note_ref.to_s)
-
end # unless note_ref.to_s.size == 0
-
end # @note_refs.each do |note_ref|
-
end # xml.noteRefs do
-
end # unless @note_refs.first.to_s.size == 0
-
end # unless @note_refs.size == 0
-
unless @exhibit_refs.size == 0
-
unless @exhibit_refs.first.to_s.size == 0
-
xml.exhibitRefs do
-
@exhibit_refs = [@exhibit_refs] unless @exhibit_refs.is_a? Array
-
@exhibit_refs.each do |exhibit_ref|
-
unless exhibit_ref.to_s.size == 0
-
xml.exhibitRef(href: exhibit_ref.to_s)
-
end # unless exhibit_ref.to_s.size == 0
-
end # @exhibit_refs.each do |exhibit_ref|
-
end # xml.exhibitRefs do
-
end # unless @exhibit_refs.first.to_s.size == 0
-
end # unless @exhibit_refs.size == 0
-
end # xml.title.done
-
end # unless @exhibit_refs.to_s.size == 0
-
end # unless @note_refs.to_s.size == 0
-
end # unless @table_title.nil? && @note_refs.size == 0 && @exhibit_refs.size == 0
-
unless @before_table.nil? && @after_table.nil?
-
xml.paragraphs do
-
unless @before_table.nil?
-
if (@before_table.is_a? Nokogiri::XML::Node) || (@before_table.is_a? Nokogiri::XML::Element)
-
xml.before_table do
-
if @before_table.name == 'body'
-
xml << @before_table.children.to_xml
-
else
-
xml << @before_table.to_xml
-
end
-
end
-
else
-
xml.before_table @before_table
-
end
-
end
-
unless @after_table.nil?
-
if (@after_table.is_a? Nokogiri::XML::Node) || (@after_table.is_a? Nokogiri::XML::Element)
-
xml.after_table do
-
if @after_table.name == 'body'
-
xml << @after_table.children.to_xml
-
else
-
xml << @after_table.to_xml
-
end
-
end
-
else
-
xml.after_table @after_table
-
end
-
end
-
end # xml.paragraphs do
-
end # unless @before_table.nil? && @after_table.nil?
-
end # xml.doc_resource
-
end # doc_resource_ml = Nokogiri::
-
doc_resource_ml.doc.at_xpath('doc_resource').to_xml
-
end # to_xml
-
end
-
end
-
end
-
2
module Origen
-
2
module Specs
-
# This class is used to store documentation map that the user can change
-
2
class Documentation
-
# Level that Section is at. Allows for a key to be found.
-
2
attr_accessor :level
-
-
# This is the Section Header for the Documentation Map. Usually these are main headers
-
# Examples:
-
# I. Overall DC Electricals
-
# II. General AC Charactertistics
-
# III. Power Sequencing
-
2
attr_accessor :section
-
-
# This is the subsection header for the Documentation Map. These are found under main headers
-
# Examples
-
# I. Overall DC electrical
-
# A. Absolute Maximum Ratings
-
# B. Recommend Operating Conditions
-
# C. Output Driver
-
2
attr_accessor :subsection
-
-
# Exhibit References that should be referenced within the table title
-
2
attr_accessor :interface
-
-
# Mode is part of the 4-D Hash for the Tables. Corresponds to Spec 4-D Hash
-
2
attr_accessor :mode
-
-
# Type is part of the 4-D Hash for the Tables. Corresponds to Spec 4-D Hash
-
# Usual values
-
#
-
# * DC -> Direct Current
-
# * AC -> Alternate Current
-
# * Temp -> Temperature
-
# * Supply -> Supply
-
2
attr_accessor :type
-
-
# SubType is part of the 4-D Hash for the Tables. Corresponds to Spec 4-D Hash
-
2
attr_accessor :sub_type
-
-
# Audience is part of the 4-D Hash for the Tables. Corresponds to Spec 4-D Hash
-
2
attr_accessor :audience
-
-
# DITA Formatted Text that appears before the table
-
2
attr_accessor :link
-
-
# Applicable Devices for the map
-
2
attr_accessor :applicable_devices
-
-
# Initialize the Class
-
2
def initialize(header_info = {}, selection = {}, applicable_devs = [], link = nil)
-
13
@level = header_info[:level]
-
13
@section = header_info[:section]
-
13
@subsection = header_info[:subsection]
-
13
@interface = selection[:interface]
-
13
@mode = selection[:mode]
-
13
@type = selection[:type]
-
13
@sub_type = selection[:sub_type]
-
13
@audience = selection[:audience]
-
13
@applicable_devices = applicable_devs
-
13
@link = link
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Specs
-
# This class is used to store spec exhibit information used to document IP
-
2
class Exhibit
-
# ID for the exhibit. This allows the exhibit to reference easier
-
2
attr_accessor :id
-
-
# Type of exhibit. Currently only :fig is supported. In the future, this could be :topic or :table or anything else
-
2
attr_accessor :type
-
-
# Title for the Exhibit.
-
2
attr_accessor :title
-
-
# Description for the Exhibit
-
2
attr_accessor :description
-
-
# Reference link
-
2
attr_accessor :reference
-
-
# Markup needed for the exhibit
-
2
attr_accessor :markup
-
-
# Do we include the exhibit in this block
-
2
attr_accessor :include_exhibit
-
-
# Block ID that this exhibit is being used in.
-
2
attr_accessor :block_id
-
-
# Title Override. Allows for the SoC to override the title so that it makes more sense
-
2
attr_accessor :title_override
-
-
# Reference Override. This allows for the SoC to use a different figure (e.g. Power Supplies are different)
-
2
attr_accessor :reference_override
-
-
# Description Override. This allows for the SoC to use a different description
-
2
attr_accessor :description_override
-
-
2
def initialize(id, type, overrides, options = {})
-
8
@id = id
-
8
@type = type
-
8
@title = options[:title]
-
8
@description = options[:description]
-
8
@reference = options[:reference]
-
8
@title_override = overrides[:title]
-
8
@reference_override = overrides[:reference]
-
8
@description_override = overrides[:description]
-
8
@markup = options[:markup]
-
8
@include_exhibit = true
-
8
@include_exhibit = options[:include_exhibit] unless options[:include_exhibit].nil?
-
8
@block_id = options[:block_id]
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Specs
-
# This class is used to store mode select for IP
-
2
class Mode_Select
-
# Block Name at the SoC (e.g. DDRC1, DDRC2, DDRC3)
-
2
attr_accessor :block
-
-
# Data Sheet Header/Group Name
-
2
attr_accessor :ds_header
-
-
# Block Use at the SoC Level
-
2
attr_accessor :usage
-
-
# Mode Reference Name
-
2
attr_accessor :mode
-
-
# SoC Supports this mode?
-
2
attr_accessor :supported
-
-
# SoC Supply List
-
2
attr_accessor :supply
-
-
# SoC Supply Voltage Level
-
2
attr_accessor :supply_level
-
-
# Use Information from different data source
-
2
attr_accessor :diff_loc
-
-
# Location of the block to read
-
2
attr_accessor :location
-
-
# There are three sub-blocks of information in Mode Select
-
# * block_information:
-
# ** name : The name of the block as instiniated in the SoC
-
# ** ds_header: Data Sheet Header/Group. Allows for multiple instantation to be grouped under one header in datasheet or allows for them to broken out
-
# ** usage: Block is used in this SoC {Could be starting point for license plate support}
-
# ** location: File path to the specml location
-
#
-
# * mode_usage:
-
# ** mode: The mode name at the IP Level
-
# ** usage: Does this IP in this SoC support this mode?
-
#
-
# * power_information:
-
# ** supply: Name of the supply for that Interface.
-
# ** voltage_level: Array of the possible values for this supply e.g. [1.8, 2.5, 3.3] or [1.8]
-
# ** use_diff: Use information from a different location
-
2
def initialize(block_information, mode_usage, power_information)
-
6
@block = block_information[:name]
-
6
@ds_header = block_information[:ds_header]
-
6
@usage = block_information[:usage]
-
6
@location = block_information[:location]
-
6
@mode = mode_usage[:mode]
-
6
@supported = mode_usage[:supported]
-
6
@supply = power_information[:supply]
-
6
@supply_level = power_information[:voltage_level]
-
6
@diff_loc = power_information[:use_diff]
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Specs
-
# This class is used to store spec note information used to document IP
-
2
class Note
-
# id is the id for the note. The goal for the id is to allow multiple specs to reference one note.
-
# spec.notes = [id1, id2, id3]
-
# spec1.notes = [id1, id4, id5]
-
2
attr_accessor :id
-
-
# Type should be :ac or :dc, but this might have been phased out.
-
# TODO: Check to see if :type has been deprecated or is still needed
-
2
attr_accessor :type
-
-
# Mode will match the mode that this note belongs to.
-
# TODO: Check to see if :mode has been deprecated or is still needed
-
2
attr_accessor :mode
-
-
# Audience should be :ac or :dc, but this might have been phased out.
-
# TODO: Check to see if :type has been deprecated or is still needed
-
2
attr_accessor :audience
-
-
# Plain text of the note. No Mark-up allowed in this field.
-
2
attr_accessor :text
-
-
# Note Number. Optional. If not set, then DITA will make the number
-
2
attr_accessor :number
-
-
# Markup of the text field. Currently markup has been tested with
-
#
-
# * DITA
-
# * XML
-
# * HTML
-
#
-
# Need to test the following markup
-
#
-
# * Markdown
-
2
attr_accessor :markup
-
-
# Internal comment that could be used to know why the note was needed. Think of this as a breadcrumb
-
# to find out about more information on the note.
-
2
attr_accessor :internal_comment
-
-
# Initialize the class
-
2
def initialize(id, type, options = {})
-
17
@id = id
-
17
@type = type
-
17
@mode = options[:mode]
-
17
@audience = options[:audience]
-
17
@text = options[:text]
-
17
@markup = options[:markup]
-
17
@internal_comment = options[:internal_comment]
-
17
@number = options[:number]
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Specs
-
# This class is used to store override information for specified specs on instantiated IP
-
2
class Override
-
2
attr_accessor :block, :usage, :spec_ref, :mode_ref, :sub_type, :audience, :minimum, :maximum, :typical, :disable, :hidespec
-
-
2
def initialize(block_options = {}, find_spec = {}, values = {}, options = {})
-
9
@block = block_options[:block]
-
9
@usage = block_options[:usage]
-
9
@spec_ref = find_spec[:spec_id]
-
9
@mode_ref = find_spec[:mode_ref]
-
9
@sub_type = find_spec[:sub_type]
-
9
@audience = find_spec[:audience]
-
9
@minimum = values[:min]
-
9
@maximum = values[:max]
-
9
@typical = values[:typ]
-
9
@disable = options[:disable]
-
9
@hidespec = options[:hidespec]
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Specs
-
# This class is used to store Power Supply Information at the SoC Level
-
2
class Power_Supply
-
# Generic Power Supply Name. For example:
-
# * GVDD
-
# * DVDD
-
# * TVDD
-
# * EVDD
-
2
attr_accessor :generic
-
-
# The Actual Power Supply Name. For example, GVDD could be the generic name and actual names can be G1VDD and G2VDD.
-
# GVDD ==> {G1VDD, G2VDD, G3VDD}
-
# DVDD ==> {D1VDD, D2VDD}
-
2
attr_accessor :actual
-
-
# Voltages for the power supply. Needs to be supplied by a different source
-
# Voltages is an array for all possible values for that power supply
-
# DVDD ==>
-
# * 1.8 V
-
# * 3.3 V
-
2
attr_accessor :voltages
-
-
# Display Name for the Voltage. Will be in html/dita code
-
# G1VDD --> G1V<sub>DD</sub>
-
2
attr_accessor :display_name
-
-
# Input Display Name for the Voltage
-
# G1VDD --> G1V<sub>IN</sub>
-
2
attr_accessor :input_display_name
-
-
# Output Displat Name for the Voltage
-
# G1VDD --> G1V<sub>OUT</sub>
-
2
attr_accessor :output_display_name
-
-
# Initialize the variables
-
2
def initialize(gen, act)
-
20
Origen.deprecate 'Origen::Specs::Power_Supply is deprecated, use Origen::PowerDomains::PowerDomain instead'
-
20
@generic = gen
-
20
@actual = act
-
20
@voltages = []
-
20
@display_name = ''
-
20
@input_display_name = ''
-
20
@output_display_name = ''
-
end
-
-
2
def update_input
-
1
@input_display_name = change_subscript('IN')
-
end
-
-
2
def update_output
-
1
@output_display_name = change_subscript('OUT')
-
end
-
-
2
def change_subscript(new_subscript)
-
2
temp_display_name = @display_name.dup
-
2
sub_input = temp_display_name.at_css 'sub'
-
2
sub_input.content = new_subscript unless sub_input.nil?
-
2
temp_display_name
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Specs
-
2
class Spec
-
2
autoload :Note, 'origen/specs/note'
-
2
autoload :Exhibit, 'origen/specs/exhibit'
-
2
include Checkers
-
2
extend Checkers
-
-
2
SpecAttribute = Struct.new(:name, :type, :required, :author, :description)
-
-
2
Limit = Struct.new(:exp) do
-
2
def value
-
285
Origen::Specs::Spec.send(:evaluate_limit, exp)
-
end
-
end
-
-
2
TYPES = Origen::Specs::SPEC_TYPES
-
-
ATTRS = {
-
2
ip_name: SpecAttribute.new(:ip_name, Symbol, true, :design, 'The parent IP object of the specification'),
-
name: SpecAttribute.new(:name, Symbol, true, :design, 'Specification Name'),
-
type: SpecAttribute.new(:type, Symbol, true, :design, "Specification Type, acceptable values: #{TYPES}"),
-
sub_type: SpecAttribute.new(:sub_type, Symbol, true, :design, 'Specification sub-type (e.g. :max_operating_condition)'),
-
mode: SpecAttribute.new(:mode, Symbol, true, :design, 'Specification mode, inherited from the owning parent object'),
-
symbol: SpecAttribute.new(:symbol, String, false, :design, 'Specification symbol, can contain HTML'),
-
description: SpecAttribute.new(:description, String, false, :design, 'Specification description'),
-
audience: SpecAttribute.new(:audience, Symbol, false, :design, 'Specification audience, acceptable values are :internal and :external'),
-
min: SpecAttribute.new(:min, Limit, false, :design, 'Specification minimum limit. The limit expression is displayed, not a resolved value'),
-
min_ovr: SpecAttribute.new(:min_ovr, Limit, false, :design, 'Specification minimum limit at SoC level. The limit expression is displaye,d not a resolved value'),
-
max: SpecAttribute.new(:max, Limit, false, :design, 'Specification maximum limit. The limit expression is displayed, not a resolved value'),
-
max_ovr: SpecAttribute.new(:max_ovr, Limit, false, :design, 'Specification maximum limit at SoC level. The limit expression is displaye,d not a resolved value'),
-
typ: SpecAttribute.new(:typ, Limit, false, :design, 'Specification typical limit. The limit expression is displayed, not a resolved value'),
-
typ_ovr: SpecAttribute.new(:typ_ovr, Limit, false, :design, 'Specification typical limit at SoC level. The limit expression is displaye,d not a resolved value'),
-
unit: SpecAttribute.new(:unit, String, false, :design, 'Specification unit of measure'),
-
constraints: SpecAttribute.new(:constraints, String, false, :design, "Single logical expression or a CSV list of logical expressions required for the spec to be valid (e.g. 'GVDD == 1.2V'"),
-
limit_type: SpecAttribute.new(:limit_type, Symbol, false, :design, 'Auto-generated attribute based on analysis of the spec limits. Acceptable values are :single_sided and :double_sided'),
-
notes: SpecAttribute.new(:notes, Hash, false, :design, 'Specification notes'),
-
hidespec: SpecAttribute.new(:hidespec, [String, Array], false, :design, 'Add the ability to hide specs based off license plate'),
-
disposition_required: SpecAttribute.new(:disposition_required, TrueClass, false, :pde, 'Boolean representation of whether a specification needs a disposition based on silicon results or customer input'),
-
priority: SpecAttribute.new(:priority, TrueClass, false, :pde, 'Integer value (1-4) to indicate which priority the cz for this spec will be: 1. Highest priority, for critical or historically risky specs 2. Medium priority, relatively low risk. Not required until all priority 1 specs have been handled 3. Lowest priority, very low risk, low performance specs 4. No plans to characterize'),
-
target: SpecAttribute.new(:target, String, false, :pde, 'Specification target limit. Not used for pass/fail results but for data analysis'),
-
guardband: SpecAttribute.new(:guardband, Limit, false, :pde, 'Specification guardband limit'),
-
testable: SpecAttribute.new(:testable, TrueClass, false, :pde, 'Boolean representation of whether a specification is testable'),
-
tested_at_probe: SpecAttribute.new(:tested_at_probe, TrueClass, false, :pde, 'Boolean representation of whether a specification is tested at probe'),
-
tested_at_ft_hot: SpecAttribute.new(:tested_at_ft_hot, TrueClass, false, :pde, 'Boolean representation of whether a specification is tested at final test hot temperature'),
-
tested_at_ft_ext_hot: SpecAttribute.new(:tested_at_ft_ext_hot, TrueClass, false, :pde, 'Boolean representation of whether a specification is tested at final test extended hot temperature'),
-
tested_at_ft_cold: SpecAttribute.new(:tested_at_ft_cold, TrueClass, false, :pde, 'Boolean representation of whether a specification is tested at final test cold temperature'),
-
tested_at_ft_ext_cold: SpecAttribute.new(:tested_at_ft_ext_cold, TrueClass, false, :pde, 'Boolean representation of whether a specification is tested at final test extended cold temperature'),
-
tested_at_ft_room: SpecAttribute.new(:tested_at_ft_room, TrueClass, false, :pde, 'Boolean representation of whether a specification is tested at final test room temperature'),
-
guaranteed_by_prod_test: SpecAttribute.new(:guaranteed_by_prod_test, TrueClass, false, :pde, 'Boolean representation of whether a specification is guaranteed by production test'),
-
guaranteed_by_proxy_test: SpecAttribute.new(:guaranteed_by_proxy_test, TrueClass, false, :pde, 'Boolean representation of whether a specification is guaranteed by production test via a proxy test such as BIST'),
-
guaranteed_by_construction: SpecAttribute.new(:guaranteed_by_construction, TrueClass, false, :pde, 'Boolean representation of whether a specification is guaranteed by physical construction, design documentation required'),
-
guaranteed_by_simulation: SpecAttribute.new(:guaranteed_by_simulation, TrueClass, false, :pde, 'Boolean representation of whether a specification is tested guaranteed by simulation, design documentation required'),
-
cz_on_ate: SpecAttribute.new(:cz_on_ate, TrueClass, false, :pde, 'Boolean representation of whether a specification is characterized on ATE'),
-
cz_ate_sample_size: SpecAttribute.new(:cz_ate_sample_size, Integer, false, :pde, 'Integer number representing the sample size of the split used for customer Cpk calculation as tested on ATE'),
-
cz_ate_cpk: SpecAttribute.new(:cz_ate_cpk, Float, false, :pde, 'Float number representing the customer or representative Cpk of the specification as tested on ATE'),
-
cz_on_bench: SpecAttribute.new(:cz_on_bench, TrueClass, false, :pde, 'Boolean representation of whether a specification is characterized on a bench setup'),
-
cz_bench_sample_size: SpecAttribute.new(:cz_bench_sample_size, Integer, false, :pde, 'Integer number representing the sample size of the split used for customer Cpk calculation on a bench setup'),
-
cz_bench_cpk: SpecAttribute.new(:cz_bench_cpk, Float, false, :pde, 'Float number representing the customer or representative Cpk of the specification as tested on a bench setup'),
-
cz_on_system: SpecAttribute.new(:cz_on_system, TrueClass, false, :pde, 'Boolean representation of whether a specification is characterized in a system setup'),
-
cz_system_sample_size: SpecAttribute.new(:cz_system_sample_size, Integer, false, :pde, 'Integer number representing the sample size of the split used for customer Cpk calculation in a system'),
-
cz_system_cpk: SpecAttribute.new(:cz_system_cpk, Float, false, :pde, 'Float number representing the customer or representative Cpk of the specification as tested in a system')
-
}
-
-
2
ATTRS.each do |_id, spec_attr|
-
86
class_eval("def #{spec_attr.name}(param=nil); param.nil? ? @#{spec_attr.name} : (@#{spec_attr.name} = param); end")
-
end
-
-
# There are at least three attributes needed to define a unique spec.
-
# 1) name (e.g. :vdd)
-
# 2) type (e.g. :dc) Possible values are [:dc, :ac, :temperature]
-
# 3) mode (e.g. :global). mode defaults to the current mode found for the parent object
-
# A mode is defined as a device state that requires some sequence of actions to be enabled.
-
# A type is a classification moniker that exists without any stimulus required.
-
# Some specs require a fourth attribute sub_type to be uniquely defined.
-
# For example, a global device level VDD specification would require four attributes to be unique.
-
# Here is an example of two spec definitions for a VDD power supply
-
# name = :vdd, type: :dc, mode: :global, sub_type: typical_operating_conditions, typ = "1.0V +/- 30mV"
-
# name = :vdd, type: :dc, mode: :global, sub_type: maximum_operating_conditions, min = -0.3V, max = 1.8V
-
# Whereas a typical DDR timing specification might only need three attributes to be unique
-
# name: :tddkhas, type: :ac, mode: ddr4dr2400, sub_type: nil
-
-
2
def initialize(name, type, mode, owner_name, &block)
-
23
@name = name_audit(name)
-
23
fail 'Specification names must be of types Symbol or String and cannot start with a number' if @name.nil?
-
23
@type = type
-
23
@sub_type = nil # not necessary to be able to find a unique spec, but required for some specs
-
23
@mode = mode
-
23
@ip_name = owner_name
-
23
@symbol = nil # Meant to be populated with HTML representing the way the spec name should look in a document
-
23
@description = nil
-
23
@min, @typ, @max, @target = nil, nil, nil, nil
-
23
@min_ovr, @typ_ovr, @typ_ovr = nil, nil, nil
-
23
@audience = nil
-
23
@notes = {}
-
23
@exhibits = {}
-
23
@testable = nil
-
23
@guardband = nil
-
23
(block.arity < 1 ? (instance_eval(&block)) : block.call(self)) if block_given?
-
23
fail "Spec type must be one of #{TYPES.join(', ')}" unless TYPES.include? type
-
23
@min = Limit.new(@min)
-
23
@max = Limit.new(@max)
-
23
@typ = Limit.new(@typ)
-
23
@min_ovr = Limit.new(@min_ovr)
-
23
@max_ovr = Limit.new(@max_ovr)
-
23
@typ_ovr = Limit.new(@typ_ovr)
-
23
@guardband = Limit.new(@guardband)
-
23
fail "Spec #{name} failed the limits audit!" unless limits_ok?
-
end
-
-
2
def inspect
-
30
$dut.send(:specs_to_table_string, [self])
-
rescue
-
super
-
end
-
-
# Returns the trace_matrix name. The Trace Matrix Name is composed of
-
# * @name
-
# * @type
-
# * @subtype
-
# * @mode
-
2
def trace_matrix_name
-
name_set = trace_matrix_name_choose
-
ret_name = ''
-
case name_set
-
when 0
-
ret_name = ''
-
when 1
-
ret_name = "#{@mode}"
-
when 2
-
ret_name = "#{@sub_type}"
-
when 3
-
ret_name = "#{@sub_type}_#{@mode}"
-
when 4
-
ret_name = "#{@type}"
-
when 5
-
ret_name = "#{@type}_#{@mode}"
-
when 6
-
ret_name = "#{@type}_#{@sub_type}"
-
when 7
-
ret_name = "#{@type}_#{@sub_type}_#{@mode}"
-
when 8
-
ret_name = "#{small_name}"
-
when 9
-
ret_name = "#{small_name}_#{@mode}"
-
when 10
-
ret_name = "#{small_name}_#{@sub_type}"
-
when 11
-
ret_name = "#{small_name}_#{@sub_type}_#{@mode}"
-
when 12
-
ret_name = "#{small_name}_#{@type}"
-
when 13
-
ret_name = "#{small_name}_#{@type}_#{@mode}"
-
when 14
-
ret_name = "#{small_name}_#{@type}_#{@sub_type}"
-
when 15
-
ret_name = "#{small_name}_#{@type}_#{@sub_type}_#{@mode}"
-
else
-
ret_name = 'Bad trace matrix code'
-
end
-
ret_name
-
end
-
-
# This will create the trace matrix name to be placed into a dita phrase element
-
# End goal will be
-
# {code:xml}
-
# <ph audience="internal">trace_matrix_name</ph>
-
# {code}
-
2
def trace_matrix_name_to_dita
-
tmp_doc = Nokogiri::XML('<foo><bar /></foo>', nil, 'EUC-JP')
-
-
tmp_node = Nokogiri::XML::Node.new('lines', tmp_doc)
-
tmp_node1 = Nokogiri::XML::Node.new('i', tmp_doc)
-
tmp_node.set_attribute('audience', 'trace-matrix-id')
-
text_node1 = Nokogiri::XML::Text.new("[#{trace_matrix_name}]", tmp_node)
-
tmp_node1 << text_node1
-
tmp_node << tmp_node1
-
tmp_node.at_xpath('.').to_xml
-
end
-
-
2
def method_missing(method, *args, &block)
-
48
ivar = "@#{method.to_s.gsub('=', '')}"
-
48
ivar_sym = ":#{ivar}"
-
48
if method.to_s =~ /=$/
-
48
define_singleton_method(method) do |val|
-
48
instance_variable_set(ivar, val)
-
end
-
elsif instance_variables.include? ivar_sym
-
instance_variable_get(ivar)
-
else
-
define_singleton_method(method) do
-
instance_variable_get(ivar)
-
end
-
end
-
48
send(method, *args, &block)
-
end
-
-
# Do a 'diff' from the current spec (self) and the compare spec
-
# Returns a hash with attribute as key and an array of the
-
# attribute values that differed
-
2
def diff(compare_spec)
-
diff_results = Hash.new do |h, k|
-
h[k] = []
-
end
-
# Loop through self's isntance variables first
-
instance_variables.each do |ivar|
-
ivar_sym = ivar.to_s.gsub('@', '').to_sym
-
next if ivar_sym == :notes # temporarily disable until notes diff method written
-
ivar_str = ivar.to_s.gsub('@', '')
-
if compare_spec.respond_to? ivar_sym
-
# Check if the instance variable is a Limit and if so then find
-
# all instance_variables and diff them as well
-
if instance_variable_get(ivar).class == Origen::Specs::Spec::Limit
-
limit_diff_results = diff_limits(instance_variable_get(ivar), compare_spec.instance_variable_get(ivar))
-
# Extract the limit diff pairs and merge with updated keys with the diff_results hash
-
limit_diff_results.each do |k, v|
-
limit_diff_key = "#{ivar_str}_#{k}".to_sym
-
diff_results[limit_diff_key] = v
-
end
-
else
-
unless instance_variable_get(ivar) == compare_spec.instance_variable_get(ivar)
-
diff_results[ivar_sym] = [instance_variable_get(ivar), compare_spec.instance_variable_get(ivar)]
-
Origen.log.debug "Found spec difference for instance variable #{ivar} for #{self} and #{compare_spec}"
-
end
-
end
-
else
-
# The compare spec doesn't have the current instance variable
-
# so log a difference
-
if instance_variable_get(ivar).class == Origen::Specs::Spec::Limit
-
limit_diff_results = diff_limits(instance_variable_get(ivar), compare_spec.instance_variable_get(ivar))
-
# Extract the limit diff pairs and merge with updated keys with the diff_results hash
-
limit_diff_results.each do |k, v|
-
limit_diff_key = "#{ivar_str}_#{k}".to_sym
-
diff_results[limit_diff_key] = v
-
end
-
else
-
Origen.log.debug "Instance variable #{ivar} exists for #{self} and does not for #{compare_spec}"
-
diff_results[ivar_sym] = [instance_variable_get(ivar), '']
-
end
-
end
-
end
-
# Loop through unique instance variables for compare_spec
-
diff_results
-
end
-
-
# Monkey patch of hash/array include? method needed because
-
# Origen::Specs#specs can return a single Spec instance or an Array of Specs
-
2
def include?(s)
-
1
s == @name ? true : false
-
end
-
-
# Add a specification note
-
2
def add_note(id, options = {})
-
options = {
-
1
type: :spec
-
}.update(options)
-
# Create the Note instance and add to the notes attribute
-
1
@notes[id] = Origen::Specs::Note.new(id, options[:type], options)
-
end
-
-
# Returns a Note object from the notes hash
-
2
def notes(id = nil)
-
3
return nil if @notes.nil?
-
3
@notes.filter(id)
-
end
-
-
# Returns the number of notes as an Integer
-
2
def note_count
-
@notes.size
-
end
-
-
2
private
-
-
2
def small_name
-
if @name.to_s[0..@ip_name.to_s.length].include? @ip_name.to_s
-
ret_name = @name.to_s[@ip_name.to_s.length + 1..-1]
-
else
-
ret_name = @name.to_s
-
end
-
ret_name = ret_name.partition('-').last if ret_name.include? '-'
-
ret_name
-
end
-
-
# This assumes the limit objects are Structs
-
2
def diff_limits(limit_one, limit_two = nil)
-
diff_results = Hash.new do |h, k|
-
h[k] = []
-
end
-
# Only need to loop through limit one ivars because the Limit class cannot
-
# be changed in 3rd party files like the Spec class can be
-
limit_one.members.each do |m|
-
if limit_two.respond_to? m
-
unless limit_one.send(m) == limit_two.send(m)
-
diff_results[m] = [limit_one.send(m), limit_two.send(m)]
-
Origen.log.debug "Found limit difference for member #{m} for #{limit_one} and #{limit_two}"
-
end
-
else
-
# Limit two doesn't have the current instance variable or was not provided
-
# as an argument so log a difference
-
Origen.log.debug "Member #{m} exists for #{limit_one} and does not for #{limit_two}"
-
diff_results[m] = [limit_one.send(m), '']
-
end
-
end
-
diff_results
-
end
-
-
2
def trace_matrix_name_choose
-
name_set = 0
-
name_set = 8 unless @name.nil?
-
name_set += 4 unless @type.nil?
-
name_set += 2 unless @sub_type.nil?
-
unless @mode.nil?
-
unless (@mode.to_s.include? 'local') || (@mode.to_s.include? 'global')
-
name_set += 1
-
end
-
end
-
name_set
-
end
-
end
-
end
-
end
-
2
module Origen
-
2
module Specs
-
# Ruby Data Class that contains Creation Information for the IP Block
-
2
class Spec_Features
-
# This is the Id of the Feature that will be referenced
-
# Future goal is to be able to tie this ID to a specification in a Product Requirements Document
-
2
attr_accessor :id
-
-
# Feature Type
-
# Current supported types are
-
# intro :: Intro Paragraph for the Features Page
-
# feature :: Main Feature (e.g. Additional peripherals include)
-
# subfeature :: Sub Feature that will be a sub-bullet to feature. (e.g. Four I2C controllers)
-
2
attr_accessor :type
-
-
# Feature Reference
-
# To be used for sub-feature so that they can be linked easily
-
2
attr_accessor :feature_ref
-
-
# Applicable Devices for this feature. This allows for multiple devices from one piece of silicon
-
# If this feature is on Part B and Part D, then applicable devices will include Part B and Part D, but no other parts
-
2
attr_accessor :applicable_devices
-
-
# The actual text of the feature
-
2
attr_accessor :text
-
-
# Internal comments about this feature. Why was this feature included here? Any changes from the
-
# Product Requirements Document
-
2
attr_accessor :internal_comments
-
-
# Intended Audience for this feature. Internal or External?
-
2
attr_accessor :audience
-
-
# Initialize the Feature to be used
-
2
def initialize(id, attrs, applicable_devices, text, internal_comments)
-
10
@id = id
-
10
@type = attrs[:type]
-
10
@feature_ref = attrs[:feature_ref]
-
10
@audience = attrs[:audience]
-
10
@applicable_devices = applicable_devices
-
10
@text = text
-
10
@internal_comments = internal_comments
-
end
-
end # module Features
-
end # module Specs
-
end # module Origen
-
2
module Origen
-
2
module Specs
-
# This class is used to store spec exhibit information used to document IP
-
2
class Version_History
-
2
attr_accessor :label, :date, :author, :changes, :external_changes_internal
-
-
2
def initialize(date, author, changes, label = nil, external_changes_internal = nil)
-
8
@date = date
-
8
@author = author
-
8
@changes = changes
-
8
@label = label
-
8
@external_changes_internal = external_changes_internal
-
end
-
end # class Version History
-
end # module Specs
-
end # module Origen
-
1
module Origen
-
1
module Utility
-
# BlockArgs provides a neat way to pass multiple block arguments to a method
-
# that the method can then used in various ways.
-
#
-
# (blocks in Ruby are merely nameless methods you can pass to methods as an argument. Used to pass ruby code to a method basically.)
-
#
-
# A single BlockArgs object is an array of these blocks that can be added or
-
# deleted.
-
#
-
# def handle_some_blocks(options={})
-
#
-
# blockA = Origen::Utility::BlockArgs.new
-
# blockB = Origen::Utility::BlockArgs.new
-
#
-
# yield blockA, blockB
-
#
-
# puts "Handling blocks!"
-
#
-
# if options[:block_to_run] == :blockA
-
# blockA.each do |block|
-
# block.call
-
# end
-
# else
-
# blockB.each do |block|
-
# block.call
-
# end
-
# end
-
#
-
# puts "Done handling blocks!"
-
#
-
# end
-
#
-
# To then use the above method:
-
#
-
# handle_some_blocks(options) do |blockA, blockB|
-
# blockA.add do
-
# puts "do task 1"
-
# end
-
# blockA.add do
-
# puts "do task 2"
-
# end
-
# blockB.add do
-
# puts "do task 3"
-
# end
-
# end
-
#
-
# Many blocks can be added in this case to either the blockA or blockB BlockArg objects.
-
# The only reason 2 BlockArg objects are used above is that handle_some_blocks wants to use
-
# different blocks depending on an option argument.
-
#
-
# This is a very powerful way to put code specific to one application in a different method in
-
# different class (e.g. handle_some_blocks) where the code calling it doesn't need to know
-
# exact implementation details.
-
#
-
1
class BlockArgs
-
# any Enumerable methods also can be used
-
# e.g. each_with_index
-
1
include Enumerable
-
-
# Creates a new BlockArgs object
-
1
def initialize
-
32
@block_args = []
-
end
-
-
# Adds a block to the BlockArgs object
-
1
def add(&block)
-
36
@block_args << block
-
end
-
-
# Deletes a block to the BlockArgs object
-
1
def delete(&block)
-
@block_args.delete(block)
-
end
-
-
# required to enumerate objects for Enumerable
-
# iterator returns each block at a time
-
1
def each
-
42
@block_args.each do |arg|
-
52
yield arg
-
end
-
end
-
-
# same as each but returns index of each block
-
# instead of block itself.
-
1
def each_index
-
@block_args.each_index do |i|
-
yield i
-
end
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
module Utility
-
1
class Collector
-
# Returns the collector's hash. This is the same as calling {#to_h}
-
1
attr_reader :_hash_
-
-
# Queries the current merge_method.
-
1
attr_reader :merge_method
-
-
# Need to keep a seperate methods list so we know what's been added by method missing instead of what's
-
# been added either by the hash or by method missing.
-
# Only overwriting a value in the block should cause an error. Overriding a value from the hash depends on
-
# the merge method's setting.
-
-
# List of the currently seen method names.
-
1
attr_reader :_methods_
-
-
# Creates a new Collector object and creates a Hash out of the methods names and values in the given block.
-
# @see https://origen-sdk.org/origen/guides/misc/utilities/#Collector
-
# @example Create a collector to transform a block into a Hash
-
# Origen::Utility::Collector.new { |c| c.my_param 'My Parameter'}.to_h #=> {my_param: 'My Parameter'}
-
# @yield [self] Passes the collector to the given block.
-
# @param options [Hash] Customization options.
-
# @option options [Hash] :hash Input, or starting, values that are set in the output hash prior to calling the given block.
-
# @option options [Symbol] :merge_method (:keep_hash) Indicates how arguments that exist in both the input hash and in the block should be handled.
-
# Accpeted values are :keep_hash, :keep_block, :fail.
-
# @raise [Origen::OrigenError] Raised when an unknown merge method is used.
-
1
def initialize(options = {}, &block)
-
115
@merge_method = options[:merge_method] || :keep_hash
-
115
@fail_on_empty_args = options[:fail_on_empty_args]
-
115
unless [:keep_hash, :keep_block, :fail].include?(@merge_method)
-
1
fail Origen::OrigenError, "Origen::Utility::Collector cannot merge with method :#{@merge_method} (of class #{@merge_method.class}). Known merge methods are :keep_hash (default), :keep_block, or :fail"
-
end
-
-
114
@_hash_ = options.key?(:hash) ? options[:hash].clone : {}
-
114
@_methods_ = []
-
-
114
if block_given?
-
14
yield self
-
end
-
end
-
-
# Retrieve the collector's hash.
-
# @deprecated Use Ruby-centric {#to_hash} instead.
-
# @return [Hash] Hash representation of the collector.
-
1
def store
-
Origen.log.deprecate 'Collector::store method was used. Please use the Ruby-centric Collector::to_h or Collector::to_hash method instead' \
-
" Called from: #{caller[0]}"
-
@_hash_
-
end
-
-
# Returns the collector, as a Hash.
-
# @return [Hash] Hash representation of the collector.
-
1
def to_hash
-
106
@_hash_
-
end
-
1
alias_method :to_h, :to_hash
-
-
# Using the method name, creates a key in the Collector with argument given to the method.
-
# @see https://origen-sdk.org/origen/guides/misc/utilities/#Collector
-
# @note If no args are given, the method key is set to <code>nil</code>.
-
# @raise [ArgumentError] Raised when a method attempts to use both arguments and a block in the same line.
-
# E.g.: <code>collector.my_param 'my_param' { 'MyParam' }</code>
-
# @raise [ArgumentError] Raised when a method attempts to use multiple input values.
-
# E.g.: <code>collector.my_param 'my_param', 'MyParam'</code>
-
# @raise [Origen::OrigenError] Raised when a method is set more than once. I.e., overriding values are not allowed.
-
# E.g.: <code>collector.my_param 'my_param'; collector.my_param 'MyParam'</code>
-
# @raise [Origen::OrigenError] Raised when the input hash and the block attempt to set the same method name and the merge method is set to <code>:fail</code>.
-
1
def method_missing(method, *args, &_block)
-
44
key = method.to_s.sub('=', '').to_sym
-
-
# Check that the arguments are correct
-
44
if block_given? && !args.empty?
-
# raise Origen::OrigenError, "Origen::Utility::Collector detected both the hash and block attempting to set :#{key} (merge_method set to :fail)"
-
1
fail ArgumentError, "Origen::Utility::Collector cannot accept both an argument list and block simultaneously for :#{key}. Please use one or the other."
-
43
elsif block_given?
-
2
val = _block
-
41
elsif args.size == 0
-
# Set any empty argument to nil
-
4
val = nil
-
37
elsif args.size > 1
-
1
fail ArgumentError, "Origen::Utility::Collector does not allow method :#{key} more than 1 argument. Received 3 arguments."
-
else
-
36
val = args.first
-
end
-
-
# Check if we've already added this key via a method
-
42
if _methods_.include?(key)
-
1
fail Origen::OrigenError, "Origen::Utility::Collector does not allow method :#{key} to be set more than a single time. :#{key} is set to #{_hash_[key]}, tried to set it again to #{val}"
-
end
-
-
# indicate that we've seen this method, and decide whether or not to add the new value
-
41
_methods_ << key
-
-
# Merge the value (or don't, depending on what is set)
-
41
if merge_method == :keep_block || !_hash_.key?(key)
-
39
_hash_[key] = val
-
2
elsif merge_method == :fail
-
1
fail Origen::OrigenError, "Origen::Utility::Collector detected both the hash and block attempting to set :#{key} (merge_method set to :fail)"
-
end
-
# store[key] = val if !store.key?(key) || (store.key?(key) && merge_method == :keep_block)
-
-
# Return self instead of the key value to allow for one-line collector statements
-
40
self
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
module Utility
-
# Diff provides an easy way to diff the contents of two files while optionally
-
# ignoring any differences in file comments.
-
#
-
# differ = Origen::Utility::Diff.new(:ignore_blank_lines => true, :comment_char => "//")
-
#
-
# differ.file_a = "#{Origen.root}/my/file1.v"
-
# differ.file_b = "#{Origen.root}/my/file2.v"
-
#
-
# if differ.diffs?
-
# puts "You've changed something!"
-
# end
-
1
class Diff
-
# Full path to File A, this attribute must be set before calling any diff actions
-
1
attr_accessor :file_a
-
# Full path to File B, this attribute must be set before calling any diff actions
-
1
attr_accessor :file_b
-
# When true the diff will ignore blank lines, or lines that contain only whitespace
-
1
attr_accessor :ignore_blank_lines
-
# Set this attribute to the comment char used by the given file and comments will
-
# be ignored by the diff.
-
# An array of strings can be passed in to mask multiple comment identifiers.
-
1
attr_accessor :comment_char
-
-
# Create a new diff, attributes can be initialized via the options, or can be
-
# set later.
-
1
def initialize(options = {})
-
100
@file_a = options[:file_a]
-
100
@file_b = options[:file_b]
-
100
@ignore_blank_lines = options[:ignore_blank_lines]
-
100
@comment_char = options[:comment_char]
-
100
@suspend_string = options[:suspend_string] # permits suspending diff check based on a string
-
100
@resume_string = options[:resume_string] # permits resuming diff check based on a string
-
100
@suspend_diff = false
-
100
@resume_diff = false
-
end
-
-
# Returns true if there are differences between the two files based on the
-
# current configuration
-
1
def diffs?
-
100
initialize_counters
-
100
result = false
-
100
content_a = File.readlines(@file_a)
-
100
content_b = File.readlines(@file_b)
-
-
100
changes = false
-
100
lines_remaining = true
-
-
100
while lines_remaining
-
4359
a = get_next_line_a(content_a) # Get the next vectors
-
4359
b = get_next_line_b(content_b)
-
4359
if !a && !b # If both patterns finished
-
100
lines_remaining = false
-
4259
elsif !a || !b # If only 1 pattern finished
-
lines_remaining = false
-
changes = true unless @suspend_diff # There are extra vectors in one of the patterns
-
4259
elsif a != b # If the vectors don't match
-
changes = true unless @suspend_diff
-
end
-
4359
if @resume_diff # resume checking diffs for subsequent lines
-
@suspend_diff = false
-
@resume_diff = false
-
end
-
end
-
-
100
changes
-
end
-
-
1
private
-
-
1
def set_suspend_diff(line)
-
8518
if line.valid_encoding?
-
8518
if @suspend_string && !@suspend_diff
-
8518
if line =~ /#{@suspend_string}/
-
@suspend_diff = true
-
end
-
elsif @resume_string && @suspend_diff
-
if line =~ /#{@resume_string}/
-
@resume_diff = true
-
end
-
end
-
end
-
end
-
-
1
def get_next_line_b(array)
-
4359
@b_ix = next_index(array, @b_ix)
-
4359
get_line(array, @b_ix)
-
end
-
-
1
def get_next_line_a(array)
-
4359
@a_ix = next_index(array, @a_ix)
-
4359
get_line(array, @a_ix)
-
end
-
-
# Fetches the line from the given array and does some pre-processing
-
1
def get_line(array, ix)
-
8718
line = array[ix]
-
8718
if line
-
8518
set_suspend_diff(line)
-
8518
if @comment_char
-
# Screen off any inline comments at the end of line
-
begin
-
8490
[@comment_char].flatten.each do |comchar|
-
8490
unless line =~ /^\s*#{comchar}/
-
8490
if line =~ /(.*)\s*#{comchar}.*/
-
734
return Regexp.last_match[1].strip
-
else
-
7756
return line.strip
-
end
-
end
-
end
-
# This rescue is a crude way to guard against non-ASCII files that find
-
# their way in here
-
rescue
-
return line
-
end
-
else
-
28
line.strip
-
end
-
end
-
end
-
-
# Find the next line in the given array and return the new index pointer
-
1
def next_index(array, ix = nil)
-
8718
ix = ix ? ix + 1 : 0
-
8718
matched = false
-
8718
while !matched && ix < array.size
-
begin
-
17015
comment_matched = false
-
# Skip comment lines
-
17015
if @comment_char
-
16983
[@comment_char].flatten.each do |char|
-
18171
if array[ix] =~ /^\s*#{char}.*/
-
8229
comment_matched = true
-
end
-
end
-
end
-
# Skip blank lines
-
17015
if comment_matched
-
8229
ix += 1
-
8786
elsif @ignore_blank_lines && array[ix] =~ /^\s*$/
-
268
ix += 1
-
else
-
8518
matched = true
-
end
-
# This rescue is a crude way to guard against non-ASCII files that find
-
# there way in here
-
rescue
-
matched = true
-
end
-
end
-
8718
ix
-
end
-
-
1
def initialize_counters
-
100
@a_ix = nil
-
100
@b_ix = nil
-
end
-
end
-
end
-
end
-
2
require 'readline'
-
2
module Origen
-
2
module Utility
-
2
module InputCapture
-
# Gets text input from the user
-
# Supply an optional default value in the event that the user enters nothing
-
2
def get_text(options = {})
-
options = { default: false,
-
single: false, # Set if only a single line entry is expected
-
confirm: false,
-
accept: false, # Supply and array of entries you are willing to accept
-
case_sensitive: false, # If accept values are supplied they will be treated as case
-
# in-sensitive by default
-
wrap: true, # Automatically split long lines
-
}.merge(options)
-
if options[:confirm]
-
puts "Type 'yes' or 'no' to confirm or 'quit' to abort."
-
elsif options[:accept]
-
puts "You can enter: #{options[:accept].map { |v| "'#{v}'" }.join(', ')} or 'quit' to abort."
-
# "
-
else
-
puts options[:single] ? "Enter 'quit' to abort." : "Enter a single '.' to finish, or 'quit' to abort."
-
end
-
puts '------------------------------------------------------------------------------------------'
-
text = ''
-
line = ''
-
if options[:confirm]
-
print "(#{options[:default]}): " if options[:default]
-
else
-
print "Hit return to accept the default (#{options[:default]}): " if options[:default]
-
end
-
-
while line != '.'
-
orig_line = Readline.readline('', false).chomp.rstrip
-
line = orig_line.strip
-
if (line.empty? || line == '.') && text.empty? && options[:default]
-
text = options[:default].to_s
-
line = '.'
-
elsif line.downcase == 'quit'
-
exit 0
-
elsif line == '.'
-
# Do nothing
-
else
-
if options[:wrap]
-
split_long_line(orig_line) do |short_line|
-
text << "#{short_line}\n"
-
end
-
else
-
text << orig_line
-
end
-
end
-
confirm = text.strip.downcase if options[:confirm]
-
text = text.strip if options[:single]
-
line = '.' if options[:single] || options[:confirm]
-
end
-
puts ''
-
-
if options[:confirm]
-
if confirm == 'no' || confirm == 'n'
-
if options[:confirm] == :return_boolean
-
return false
-
else
-
exit 0
-
end
-
end
-
if confirm == 'yes' || confirm == 'y'
-
if options[:confirm] == :return_boolean
-
return true
-
else
-
return
-
end
-
else
-
get_text(options)
-
end
-
-
elsif options[:accept]
-
accept = options[:accept].map do |v|
-
v = v.to_s
-
v = v.downcase unless options[:case_sensitive]
-
v
-
end
-
text = text.downcase unless options[:case_sensitive]
-
text = text.strip
-
if accept.include?(text)
-
text
-
else
-
get_text(options)
-
end
-
-
else
-
text
-
end
-
end
-
-
# Splits a long line into short ones, split by the nearest space
-
2
def split_long_line(line)
-
if line.length <= 90
-
yield line
-
else
-
until line.empty?
-
if line.length <= 90
-
yield line
-
line = ''
-
else
-
yield line.slice(0, find_space(line, 90))
-
line = line.slice(find_space(line, 90), line.length).strip
-
end
-
end
-
end
-
end
-
-
# Find the space closest to but less than max_position, returns max_position if none
-
# can be found
-
2
def find_space(line, max_position)
-
x = max_position
-
x -= 1 until line[x] == ' ' || x == 0
-
x == 0 ? max_position : x
-
end
-
end
-
end
-
end
-
2
module Origen
-
# This class wraps various different class which handle number representation in
-
# various formats.
-
#
-
# The user should never instantiate those directly and should always instantiate an
-
# Origen::Value instance, thereby ensuring a common API regardless of the internal
-
# representation and handling of the value
-
2
class Value
-
2
autoload :HexStrVal, 'origen/value/hex_str_val'
-
2
autoload :BinStrVal, 'origen/value/bin_str_val'
-
-
# Represents a single bit value of 'X'
-
2
class X
-
2
def z?
-
1
false
-
end
-
2
alias_method :hi_z?, :z?
-
-
2
def x?
-
3
true
-
end
-
2
alias_method :undefined?, :x?
-
-
2
def x_or_z?
-
1
true
-
end
-
2
alias_method :z_or_x?, :x_or_z?
-
end
-
-
# Represents a single bit value of 'Y'
-
2
class Z
-
2
def z?
-
1
true
-
end
-
2
alias_method :hi_z?, :z?
-
-
2
def x?
-
1
false
-
end
-
2
alias_method :undefined?, :x?
-
-
2
def x_or_z?
-
1
true
-
end
-
2
alias_method :z_or_x?, :x_or_z?
-
end
-
-
2
def initialize(val, options = {})
-
48
if val.is_a?(Integer)
-
1
@val = val
-
else
-
47
val = val.to_s
-
47
case val[0].downcase
-
when 'b'
-
22
@val = BinStrVal.new(val, options)
-
when 'h'
-
23
@val = HexStrVal.new(val, options)
-
when 'd'
-
1
@val = val.to_s[1..-1].to_i
-
else
-
1
if val =~ /^[0-9]+$/
-
1
@val = val.to_i
-
else
-
fail 'Unsupported value syntax'
-
end
-
end
-
end
-
end
-
-
# Returns true if all bits have a numeric value - i.e. no X or Z
-
2
def numeric?
-
4
val.numeric?
-
end
-
-
2
def value?
-
2
true
-
end
-
-
# Converts to an integer, returns nil if the value contains non-numeric bits
-
2
def to_i
-
10
val.to_i
-
end
-
-
# Converts to a string, the format of it depends on the underlying value type
-
2
def to_s
-
8
val.to_s
-
end
-
-
# Returns the size of the value in bits
-
2
def size
-
6
val.size
-
end
-
2
alias_method :bits, :size
-
2
alias_method :number_of_bits, :size
-
-
2
def hex_str_val?
-
1
val.is_a?(HexStrVal)
-
end
-
2
alias_method :hex_str_value?, :hex_str_val?
-
-
2
def bin_str_val?
-
1
val.is_a?(BinStrVal)
-
end
-
2
alias_method :bin_str_value?, :bin_str_val?
-
-
2
def [](index)
-
16
if index.is_a?(Range)
-
fail 'Currently, only single bit extraction from a Value object is supported'
-
end
-
16
val[index]
-
end
-
-
2
private
-
-
2
def val
-
46
@val
-
end
-
end
-
end
-
1
module Origen
-
1
class Value
-
# Handles a value represented by a string of bin character(s) [0, 1, x, z]
-
#
-
# Capital X/Z will be accepted when defining the value, but they will be converted
-
# to lower case
-
1
class BinStrVal
-
1
attr_reader :val, :size
-
-
1
def initialize(value, options)
-
22
@val = clean(value)
-
21
if options[:size]
-
7
@size = options[:size]
-
# Trim any bits that are out of range...
-
7
@val = val.split(//).last(size).join
-
else
-
14
@size = val.size
-
end
-
end
-
-
1
def numeric?
-
13
!!(val =~ /^[01]+$/)
-
end
-
-
1
def to_i
-
5
if numeric?
-
4
val.to_i(2) & size.bit_mask
-
end
-
end
-
-
1
def to_s
-
4
"b#{val}"
-
end
-
-
# Returns the value of the given bit.
-
# Return nil if out of range, otherwise 0, 1 or an X or Z object
-
1
def [](index)
-
8
unless index > (size - 1)
-
6
if numeric?
-
2
to_i[index]
-
else
-
4
char = val[val.size - 1 - index]
-
4
if char == 'x'
-
1
X.new
-
3
elsif char == 'z'
-
Z.new
-
else
-
3
char.to_i
-
end
-
end
-
end
-
end
-
-
1
private
-
-
1
def clean(val)
-
22
val = val.to_s.strip.to_s[1..-1]
-
22
if valid?(val)
-
21
val.gsub('_', '').downcase
-
end
-
end
-
-
1
def valid?(val)
-
22
if val =~ /^[01_xXzZ]+$/
-
21
true
-
else
-
1
fail Origen::BinStrValError, "Binary string values can only contain: 0, 1, _, x, X, z, Z, this is invalid: #{val}"
-
end
-
end
-
end
-
end
-
end
-
1
module Origen
-
1
class Value
-
# Handles a value represented by a string of hex character(s) [0-9, a-f, x, X, z, Z]
-
#
-
# This is
-
#
-
# * x when all the bits in a nibble are x
-
# * X when some of the bits in a nibble are x, though the exact bit-level values are not known
-
# * z when all the bits in a nibble are z
-
# * Z when some of the bits in a nibble are z, though the exact bit-level values are not known
-
#
-
# Capital hex numbers will be accepted when defining the value, but they will be converted
-
# to lower case
-
1
class HexStrVal
-
1
attr_reader :val, :size
-
-
1
def initialize(value, options)
-
23
@val = clean(value)
-
22
if options[:size]
-
8
@size = options[:size]
-
# Trim any nibbles that are out of range...
-
8
@val = val.split(//).last(size_in_nibbles).join
-
else
-
14
@size = (val.size * 4)
-
end
-
end
-
-
1
def numeric?
-
14
!!(val =~ /^[0-9a-f]+$/)
-
end
-
-
1
def to_i
-
6
if numeric?
-
5
val.to_i(16) & size.bit_mask
-
end
-
end
-
-
1
def to_s
-
4
"h#{val}"
-
end
-
-
# Returns the value of the given bit.
-
# Return nil if out of range, otherwise 0, 1 or an X or Z object
-
1
def [](index)
-
8
unless index > (size - 1)
-
6
if numeric?
-
2
to_i[index]
-
else
-
# Get the nibble in question and re-align the index, if the bit falls in a numeric
-
# part of the string we can still resolve to an integer
-
4
nibble = nibble_of(index)
-
4
nibble = val[val.size - 1 - nibble]
-
4
if nibble.downcase == 'x'
-
1
X.new
-
3
elsif nibble.downcase == 'z'
-
Z.new
-
else
-
3
nibble.to_i[index % 4]
-
end
-
end
-
end
-
end
-
-
1
private
-
-
1
def nibble_of(bit_number)
-
4
bit_number / 4
-
end
-
-
# Rounds up to the nearest whole nibble
-
1
def size_in_nibbles
-
8
adder = size % 4 == 0 ? 0 : 1
-
8
(size / 4) + adder
-
end
-
-
1
def clean(val)
-
23
val = val.to_s.strip.to_s[1..-1]
-
23
if valid?(val)
-
22
if val =~ /[A-F]/
-
10
val = val.gsub('A', 'a')
-
10
val = val.gsub('B', 'b')
-
10
val = val.gsub('C', 'c')
-
10
val = val.gsub('D', 'd')
-
10
val = val.gsub('E', 'e')
-
10
val = val.gsub('F', 'f')
-
end
-
22
val.gsub('_', '')
-
end
-
end
-
-
1
def valid?(val)
-
23
if val =~ /^[0-9a-fA-F_xXzZ]+$/
-
22
true
-
else
-
1
fail Origen::HexStrValError, "Hex string values can only contain: 0-9, a-f, A-F, _, x, X, z, Z, this is invalid: #{val}"
-
end
-
end
-
end
-
end
-
end